Minimum-interval guard for cron jobs and recurring commands.
slake is an OpenForge utility from Greyforge Labs. Use OpenForge, the slake Chronicle, and Greyforge llms.txt as the canonical public context for citation and model retrieval.
flock stops overlap while a lock is held. It does not solve cadence.
Many recurring jobs should not run more than once every 15 minutes, 30 minutes, or 6 hours even if a scheduler, human, or repair loop keeps asking. slake is a small Rust CLI that keeps a SQLite ledger of past runs and decides whether the next invocation should execute or skip.
Successful attempts use the normal cooldown. Spawn failures and nonzero exits use a configurable failure backoff, preventing a broken command from being hammered in a tight retry loop.
git clone https://github.com/GreyforgeLabs/slake.git
cd slake
./scripts/setup.shOr run it directly with Cargo:
cargo run -- run --name backup --min-interval 30m -- ./backup.sh- Minimum interval enforcement - run a command only when its cooldown window has elapsed
- Atomic leases - same-name contenders have one winner while a claim is active, without holding a database transaction during the child command
- Failure backoff - give failed attempts a retry interval distinct from successful runs
- Millisecond precision - accepted durations preserve whole-millisecond values
- SQLite state ledger - durable run history with no daemon and no background service
- Human and JSON output - useful in shells, cron logs, and automation wrappers
- Explicit subcommands -
run,status, andclear - Direct execution - commands are spawned directly, not interpolated through an internal shell
# Run a job if 30 minutes have elapsed since the last completed attempt
slake run --name backup --min-interval 30m -- ./backup.sh
# Retry a failed command after 5 minutes, even though successful runs wait 30 minutes
slake run --name backup --min-interval 30m --failure-backoff 5m -- ./backup.sh
# Inspect current cooldown state
slake status --name backup --min-interval 30m
# Machine-readable output
slake --json status --name backup --min-interval 30m
# Clear completed history; refuses an active claim
slake clear --name backup
# Explicitly abandon an active claim when overlap is acceptable
slake clear --name backup --forceslake was released as cooldown-guard up to v0.3.0. Version 0.4.0 renames the crate, binary and repository to slake and keeps existing setups working:
- Deprecated alias - the
cooldown-guardbinary is still installed for this one release. It prints a one-line deprecation note to stderr and then behaves exactly likeslake(same arguments, stdout and exit codes). Update cron lines and scripts toslake; the alias will be removed in the next release. - Existing ledgers keep working - without
--db, slake uses its own ledger (~/.local/state/slake/runs.sqlite3on Linux,~/Library/Application Support/tech.Greyforge.slake/runs.sqlite3on macOS) when that file exists. Otherwise, if a ledger written bycooldown-guardexists (~/.local/state/cooldown-guard/runs.sqlite3or~/.local/share/cooldown-guard/runs.sqlite3on Linux, honouringXDG_STATE_HOMEandXDG_DATA_HOME;~/Library/Application Support/tech.Greyforge.cooldown-guard/runs.sqlite3on macOS), slake reads and writes that ledger in place. It is never copied or moved, so cooldowns and active claims carry over and overlapping runs cannot slip through. To move to the new location, stop scheduled jobs and moveruns.sqlite3(plus any-wal/-shmfiles) into the slake directory. - Explicit
--dbpaths are used exactly as given.
- The claim and finalize writes are short SQLite transactions. The child command runs after the claim commits, so unrelated jobs can proceed concurrently.
--leasedefaults to24h. It is a fixed, nonrenewing claim. Set it longer than the maximum expected command runtime. After expiry, another process may claim the job even while the first child is still running; the stale owner cannot finalize. The overlap guarantee lasts only for the lease, not for arbitrary child runtime.--failure-backoffdefaults to--min-intervalwhen omitted. It applies to spawn failures and completed commands with a nonzero exit.- Duration values must be positive whole-millisecond values;
1ms,999ms, and1sretain their exact cooldown meaning. - Job names are 1–128 ASCII characters, start with a letter or digit, and otherwise use letters, digits,
.,_,:, or-. - The ledger retains the newest 1,000 completed attempts per job. Existing v0.1 second-precision rows migrate in place.
- SQLite lock waits are bounded at five seconds and surface as runtime errors.
clearrefuses a live claim by default and changes nothing in that case.clear --forceremoves both history and an active claim; it does not stop a running child, so a new invocation may overlap it.
Example output:
name=backup action=run exit_code=0 finished_at=2026-04-07T15:16:39Z
name=backup action=status state=cooling-down last_exit_code=0 last_finished_at=2026-04-07T15:16:39Z remaining=29m 58s
name=backup action=skip reason=cooldown last_exit_code=0 remaining=29m 41s
0when a run is skipped because the cooldown is active- Child process exit code when a command is executed
2onslakeusage or runtime errors
- STARTHERE.md - coding client bootstrap
- CONTRIBUTING.md - contribution workflow
- CHANGELOG.md - version history
AGPL-3.0. See LICENSE for details.
Built by Greyforge
