A self-hostable scheduling primitive: create a schedule over an API, and it reliably fires an HTTP/queue target at the right time — with timezone/DST, retries, and a dead-letter path correct out of the box.
gatilho is Portuguese for "trigger" — what the engine does when a schedule comes due.
Licensed AGPL-3.0-or-later.
One binary. An embedded file for storage (no Postgres or Redis to deploy) — ever. Scale from one node to an HA cluster by running more of the same binary as a Raft group; still no external store.
A scheduled job is just an event you publish to the future. "Fire at 3pm" = "deliver this event, delayed until 3pm." "Every night" = "deliver this event on a cron generator." So the scheduler is not a separate thing from an event bus — it is the same delivery engine with a time trigger instead of a now trigger. The scheduler is the front door; a content-routing event bus is a natural follow-on that reuses the same delivery half.
- Not static/recurring infra cron (k8s CronJob, dkron, systemd timers already do that). The target is the dynamic, high-cardinality, per-entity, one-time, timezone-aware case: "fire a distinct callback for each of millions of users at their own local time, created and cancelled at runtime via API."
- Not a workflow/durable-execution engine (Temporal / Hatchet / Inngest). This is the dedicated scheduling primitive, not an orchestration platform.
Because it is a genuinely interesting systems problem: fire millions of distinct future timestamps, precisely, durably, at-least-once (dedupe by idempotency key) across restarts, without holding them all in RAM — and get timezone/DST math right. Everyone hand-rolls a Postgres SKIP LOCKED poller or a Redis sorted-set and rebuilds leasing, retries, DLQ, and DST badly each time. Doing it once, cleanly, in a single dependency-free binary is a fun thing to build well.
See docs/ for the concept, prior art, and architecture.