Single-binary Crystal cron scheduler. Web UI, REST API, remote workers, GitHub sync, AI jobs, and audit trail — one process, ~15 MB idle.
git clone https://github.com/kdairatchi/CronLord
cd CronLord
shards install
shards build --release
./bin/cronlord serverOpen http://localhost:7070.
docker run -p 7070:7070 \
-v cronlord-data:/var/lib/cronlord \
ghcr.io/kdairatchi/cronlord:latest serverThe GHCR image is signed with cosign, carries an SPDX SBOM and SLSA provenance, and is Trivy-scanned for HIGH/CRITICAL CVEs before signing.
Download a release tarball from the releases page, verify, and install:
curl -fsSL https://github.com/kdairatchi/CronLord/releases/latest/download/install.sh | shinstall.sh checks the .sha256 sidecar before writing anything to disk.
| Area | What ships |
|---|---|
| Scheduler | Tickless, IANA timezones, DST-correct, macro expressions (@hourly, @daily, @weekly, …) |
| Job kinds | shell (subprocess), http (outbound request), claude (prompt → claude -p) |
| Workers | Remote workers over HMAC-signed lease protocol; reference worker in the same binary |
| Run control | Cancel queued or running jobs from UI or API |
| Notifications | Webhook and Slack channels, per-job configuration |
| Logging | Per-run stdout/stderr capture; SSE live tail with line numbers, ANSI color, copy, auto-scroll |
| GitHub webhook | POST /webhooks/github — HMAC-SHA256 verified, fires jobs tagged category=github:push |
| GitHub sync | Fetch a cronlord.toml from any repo and upsert jobs tagged source=github |
| Observability | Prometheus /metrics, audit trail at /audit, healthcheck at /healthz |
| Self-check | cronlord doctor — 13 probes covering DB, config, workers, tzdata, security posture |
| UI | Icon sidebar, action dropdowns per job row, toast notifications, empty states, settings wizard |
| Supply chain | cosign signature, SPDX SBOM, SLSA provenance, .sha256 release sidecars |
| Storage | SQLite only — no external database required |
Dashboard — queued, running, recent runs, failure rate at a glance.
Jobs — schedule, next fire in local TZ, kind, enabled state, action dropdown.
Edit — cron expression, timezone, kind, working dir, env vars, notifications.
Live runs — pulsing running-status badge, cancel button per run.
Job running — real-time status with elapsed time.
Run log — SSE live tail, line numbers, ANSI color, copy button, auto-scroll.
Runs history — exit code, duration, log link per run.
Copy and edit the example:
cp cronlord.toml.example cronlord.tomlEnvironment variables override every key (CRONLORD_HOST, CRONLORD_PORT, CRONLORD_DATA, CRONLORD_DB, CRONLORD_LOG_DIR, CRONLORD_ADMIN_TOKEN).
[server]
host = "127.0.0.1"
port = 7070
## Require Authorization: Bearer <token> on /api/*
## Generate: openssl rand -hex 32
admin_token = "replace-me-with-a-long-random-string"
[storage]
## SQLite DB and per-run logs live here
data_dir = "/var/lib/cronlord"
# db_path = "/var/lib/cronlord/cronlord.db" # override DB location
# log_dir = "/var/lib/cronlord/logs" # override log location
## GitHub integration — controls both webhook trigger and job sync
[github]
webhook_secret = "replace-me-with-a-long-random-string" # for POST /webhooks/github
## Job sync: fetch [[jobs]] from a remote TOML and upsert into the DB
repo = "owner/repo" # required
branch = "main"
path = "cronlord.toml" # path within the repo
token = "ghp_..." # PAT for private repos; omit for public
## Declarative jobs — re-upserted at boot, tagged source = "toml"
[[jobs]]
id = "heartbeat"
name = "Heartbeat"
schedule = "*/1 * * * *"
command = "date -u"
kind = "shell"
enabled = trueNote
Jobs defined in cronlord.toml are re-upserted on every boot. Deleting them from the UI is non-destructive — they return on restart. Use the UI or API for jobs you want full control over.
Runs a subprocess. command is passed to /bin/sh -c.
[[jobs]]
id = "nightly-backup"
name = "Nightly DB dump"
schedule = "0 3 * * *"
kind = "shell"
command = "/usr/local/bin/backup.sh --dest s3://mybucket/$(date +%F).tar.gz"
timeout_sec = 1800
enabled = truestdout and stderr are captured to a per-run log file and streamable via SSE at GET /api/runs/:id/log.
Fires an outbound HTTP request. Useful for polling endpoints or calling webhooks.
[[jobs]]
id = "health-check"
name = "API health check"
schedule = "*/5 * * * *"
kind = "http"
command = "POST https://api.example.com/health"
enabled = trueNote
The http runner enforces SSRF guards — requests to RFC-1918 private ranges are blocked by default. The Doctor check private_nets_guard verifies this is active.
Passes command as a prompt to claude -p. Requires the Claude CLI on $PATH.
[[jobs]]
id = "nightly-digest"
name = "Nightly AI digest"
schedule = "0 7 * * *"
kind = "claude"
command = "Summarize the last 24 hours of /var/log/app.log and output a 5-bullet digest."
enabled = trueThe full model response is captured as the run log. The Doctor claude_cli probe confirms the binary is present and executable before you schedule jobs of this kind.
Receive GitHub events and trigger matching jobs automatically.
1. Generate a webhook secret
openssl rand -hex 322. Add to cronlord.toml
[github]
webhook_secret = "your-generated-secret"3. Register in GitHub repo settings
- Payload URL:
https://your-cronlord-host/webhooks/github - Content type:
application/json - Secret: the value from step 1
- Events: select the events you want (push, release, etc.)
4. Tag jobs to fire on push
Add category=github:push as a label to any job (via UI or API). On a verified push event, all matching jobs are queued immediately.
CronLord validates every incoming webhook with HMAC-SHA256 against the X-Hub-Signature-256 header. Requests with an invalid or missing signature are rejected with 401.
Keep your job definitions in version control. CronLord fetches a cronlord.toml from any GitHub repo and upserts the [[jobs]] entries into the local DB, tagged source=github.
1. Create a cronlord.toml in your repo
[[jobs]]
id = "deploy-staging"
name = "Deploy to staging"
schedule = "0 2 * * 1-5"
kind = "shell"
command = "/deploy/staging.sh"
enabled = true2. Configure sync in cronlord.toml
[github]
repo = "myorg/myrepo"
branch = "main"
path = "cronlord.toml"
token = "ghp_..." # omit for public repos3. Trigger sync
From the CLI:
cronlord github syncFrom the API:
curl -X POST http://localhost:7070/api/github/sync \
-H "Authorization: Bearer $CRONLORD_ADMIN_TOKEN"From the UI: navigate to GitHub in the sidebar and click Sync now.
Tip
Wire a github:push webhook job that runs cronlord github sync to get automatic re-sync on every push to the config repo.
CronLord dispatches jobs to workers over a signed lease protocol. The reference worker ships in the same binary.
Register a worker token (via settings or directly in config):
openssl rand -hex 32
# Add to cronlord.toml or pass as CRONLORD_WORKER_TOKENRun the reference worker:
cronlord worker run \
--url http://localhost:7070 \
--token your-worker-tokenAuth flow:
- Worker calls
POST /api/worker/leasewith its token inAuthorization: Bearer. - Server responds with a signed lease (HMAC-SHA256 over job ID + expiry).
- Worker runs the job, posts results back with the same lease — any tampered or expired lease is rejected.
Workers heartbeat to /api/worker/heartbeat. The Doctor workers_heartbeat probe flags workers that have gone silent.
All /api/* routes require Authorization: Bearer <admin_token> when admin_token is set.
| Method | Path | Description |
|---|---|---|
GET |
/api/jobs |
List all jobs |
POST |
/api/jobs |
Create a job |
GET |
/api/jobs/:id |
Get a job |
PUT |
/api/jobs/:id |
Update a job |
DELETE |
/api/jobs/:id |
Delete a job |
POST |
/api/jobs/:id/run |
Trigger a job immediately |
GET |
/api/runs |
List recent runs |
GET |
/api/runs/:id |
Get a run |
GET |
/api/runs/:id/log |
SSE log stream for a run |
POST |
/api/runs/:id/cancel |
Cancel a queued or running job |
POST |
/api/github/sync |
Trigger a GitHub job sync |
POST |
/webhooks/github |
GitHub event webhook receiver |
GET |
/metrics |
Prometheus metrics |
GET |
/healthz |
Health check (no auth required) |
GET |
/audit |
Audit trail |
cronlord doctor runs 13 self-checks and prints a status report with remediation hints for anything failing.
cronlord doctorAlso available in the web UI at /doctor.
| Probe | What it checks |
|---|---|
binary |
Binary present and executable |
config |
Config file parses without error |
data_dir |
Data directory is writable |
db_integrity |
SQLite PRAGMA integrity_check passes |
pending_migrations |
No unapplied schema migrations |
log_dir_size |
Log directory is not consuming excessive disk |
stuck_runs |
No runs have been in running state beyond their timeout |
workers_heartbeat |
All registered workers have heartbeated recently |
tzdata |
IANA timezone data is available on the host |
admin_token |
Admin token is set and non-trivial |
private_nets_guard |
SSRF protection for http jobs is active |
claude_cli |
claude binary is on $PATH (required for claude job kind) |
github_webhook |
Webhook secret is configured if GitHub integration is in use |
Run doctor first when troubleshooting — it surfaces the most common failure modes with direct links to troubleshooting docs.
Worker auth: Workers authenticate with a bearer token. The server issues HMAC-SHA256 signed leases per job dispatch. A worker cannot forge a lease for a job it was not assigned, and expired leases are rejected.
Admin API: All /api/* routes are protected by Authorization: Bearer <admin_token> when admin_token is set in config. Without it, the API is open — set a token in any production deployment.
GitHub webhook: Every inbound event is validated against X-Hub-Signature-256 using the configured webhook_secret. Events with missing or invalid signatures are rejected before any job is queued.
SSRF guard: The http job runner blocks requests to RFC-1918 private address ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and loopback. The Doctor private_nets_guard probe confirms this is active.
Supply chain: Release binaries carry .sha256 sidecars. The GHCR image is signed with cosign; signature, SBOM, and SLSA provenance are attached to the image manifest.
Report suspected vulnerabilities privately — see SECURITY.md.
Browse at https://kdairatchi.github.io/CronLord or read in-tree under docs/:
getting started · CLI · job kinds · API · deployment · troubleshooting · architecture · comparison · contributing
MIT
