Recurring: cron(...)
Declare a cron in your manifest. It fires a function every time the schedule matches (the scheduler checks once a minute).
app.ts
functions/: a normal query, mutation, or action:
functions/hourlyRollup.ts
Make cron functions
internal: true. A cron function is also a regular function, so without internal: true, it is reachable at /api/fn/<name>. Marking it internal means only the scheduler (and ctx.runMutation) can invoke it.ctx.db.* calls run server-side and are not subject to entity policies (those gate client sync, not trusted server code), so a maintenance cron reads and writes its entities directly.
Elevate only in two cases, and always with an audit reason:
reason is mandatory. Every elevation is logged, so an operator can audit who elevated and why. Pylon never grants admin to a cron implicitly.
Schedule format. Standard 5-field cron: minute hour day-of-month month day-of-week. A schedule that does not parse, or a function name that is not registered, is logged loudly at boot and skipped. A typo fails visibly in pylon dev instead of silently never running.
Durability. Cron jobs are backed by a persistent job store. A fire that is in flight survives a restart, and the schedule resumes after a redeploy. Postgres stores the queue in the application database. SQLite stores it in a local sidecar database.
On Postgres, all replicas use one shared queue. Workers use row locks and short leases to claim jobs. Another replica can recover a job after its lease expires. One elected cron scheduler adds each matching cron to the shared queue once per tick. During a rolling release, each replica claims only functions that exist in that release.
SQLite jobs stay on one machine. Do not run one SQLite database on multiple replicas.
One-shot: ctx.scheduler
To run something later (not on a repeating schedule), schedule it from inside a mutation or action:
cancel resolves { cancelled: true } when the job will not run. It
resolves { cancelled: false } when the id is unknown or the job already
started or finished; a running job is not interrupted. It rejects with
SCHEDULE_CANCEL_FAILED when the cancel could not be saved, and the job
then still runs. Cancelled jobs stay cancelled across restarts and on
every Postgres replica.
Store the id to cancel later, for example on the appointment row:
runAfter: if the mutation throws, the job stays scheduled. A job
the same mutation scheduled is removed before it is ever enqueued. From
an action, a cancel takes effect at once.
The scheduled function runs with the auth identity of the caller that scheduled it. Pylon enforces this identity at schedule time. The args you pass are delivered verbatim. Like crons, these scheduled calls are durable across restarts.
When a Postgres mutation calls runAfter or runAt, Pylon commits the scheduled job in the same database transaction as the mutation writes. A rollback removes both the writes and the job. Scheduling from an action cannot share a transaction with its external work.