trigger.schedule does not fire on its own - something has to be watching the clock. That something is px0d, a deliberately dumb scheduler: it polls workflows/, evaluates cron expressions in machine local time, spawns px0 workflows run <id> --quiet for anything due, and runs one housekeeping pass a day.
Dumb is the goal here. There is no queue to inspect, no job state to reconcile, and nothing to recover from a corrupt scheduler database - the workflow files are the schedule.
1. Give a workflow a schedule
A scheduled workflow must write to a file (
output: {target: file, path: ...}). Nothing is watching stdout when the run fires, so px0 treats stdout-plus-schedule as a validation error rather than losing the output.Or watch instead of scheduling
A workflow does not have to fire on the clock at all.trigger.watch polls a read-only tool and runs the workflow when an item it has not seen before turns up:
2. Install the daemon
Force the cron fallback with
--fallback-cron if you would rather not use the native one.
status tells you whether the process is genuinely alive - it signals the recorded pid rather than trusting a pidfile - plus the last fire per workflow and each scheduled workflow’s next upcoming fire. That last part is the quickest way to sanity-check a cron expression you are unsure about.
To run it in the foreground instead, which is what you want while debugging a schedule:
3. What it does while it runs
The loop polls every 30 seconds. On each tick it fires anything whose cron schedule is due, and polls anytrigger.watch whose interval has elapsed, firing on anything new. A workflow with enabled: false is skipped by both, and by the cron fallback’s generated crontab block. On the first tick of a new calendar day it also runs the nightly pass:
Every one of those steps is fault-isolated. A broken index or an unreachable playlist is recorded in the report, and the rest of housekeeping still runs - one bad URL should not cost you a week of version checkpoints.
4. Missed fires
If your machine was asleep or the daemon was down, the fires it missed are not silently dropped. On startup and on every tick, px0 compares each workflow’s schedule against its last recorded fire, runs what it owes you, and marks anything discovered well after it was due as late in the run record. So a laptop that was closed on Friday afternoon still gets its Friday digest when it wakes up - and the record tells you it arrived late, so you are not left wondering why a 5pm digest is timestamped Saturday morning.5. Watch what it is doing
logs.path. There is no rotation yet, so if it ever gets unwieldy, truncate it - nothing reads it back.
For one run rather than the daemon as a whole:
--follow on a run that has already finished prints what is there and returns immediately rather than hanging, so it is safe to use on any run id without checking first.
6. Scheduled runs are just runs
Everything a scheduled fire does is recorded exactly like a manual run, withtrigger: schedule on the record:
Running without the daemon
You do not have to install it. Without the daemon,trigger.schedule and trigger.watch are documentation, every run is one you started, and the nightly housekeeping simply does not happen - which mostly means hand edits are checkpointed on the next run instead of overnight, and you reindex the brain yourself with px0 brain reindex.
If your machine already has a scheduler you trust, pointing it at px0 workflows run <id> --quiet is a perfectly reasonable substitute.
Uninstalling the scheduler
install put in place - the launchd plist or the systemd unit, unloaded first - and stops the daemon if it is running. Workflows keep their schedules; nothing fires on its own again until px0 daemon install runs again. On the cron fallback, it says that px0’s entries are still in your crontab rather than editing it - that file is yours. install.sh --uninstall also removes the scheduler unit, but it removes px0 along with it.
Retries and failure notifications
A workflow can be told how many times to retry itself and how to tell you when it still fails, in its ownretry and on_failure frontmatter, or store-wide with px0 config set:
on_failure block wins over these, so the noisy hourly job can stay quiet while the nightly report shouts. See Workflow files.
