For developers
Background jobs
The worker process, every scheduled and on-demand job, when it runs and what it does.
The worker
Jobs run in a separate process from the panel, on the same database, using pg-boss:
pnpm --filter @harbor/app worker # pnpm dev starts it too
pnpm --filter @harbor/app job <name> # run one job now
The panel only enqueues jobs (apps/app/lib/jobs/queue.ts). If the worker is down, the panel
keeps working and jobs wait in the queue until it is back. The worker owns pg-boss maintenance and
the cron schedule. The decisions about which carrier, load or invoice deserves an alert live in
packages/core/jobs.ts, so they are unit-tested; the handlers are in apps/app/lib/jobs/.
Schedules run in JOBS_TIMEZONE (default America/Chicago).
Scheduled jobs
| Job | Runs | Does |
|---|---|---|
lfd-alerts | Daily 06:00 | For each container whose last free day is 2 days away or less (and not yet delivered or cancelled), notifies the load's agent and operations. Once per load per day |
invoice-aging | Daily 06:15 | Notifies finance when an invoice crosses 30, 60 or more days past due |
lane-alerts | Daily 06:30 | Notifies managers of lane rates under the target margin and contract rates about to expire (Lanes) |
monthly-report | 07:00 on the 1st | Emails each company's owners last month's summary (loads delivered, revenue, carrier cost, margin). Once per month per company |
housekeeping | Daily 03:30 | Deletes old sign-in attempts and expired invite, reset and confirmation links. Not tenant data, so the counts go to the worker log |
trial-check | Daily 05:45 | Warns admins whose free trial ends soon, and moves an expired trial to past due (read-only until a plan is chosen) |
org-erase | Daily 03:45 | Permanently erases companies closed more than 30 days ago |
On-demand jobs
| Job | Triggered by | Does |
|---|---|---|
carrier-compliance-reminder | Creating a carrier | 15 minutes later, if the carrier is still pending compliance, alerts the compliance role (R11) |
autopilot | One of the 7 business events | Runs the company's Auto pilot rules for that event and logs every run |
org-export | "Export all data" on the account page | Builds the company's full export, stores it, and tells the person who asked (bell and email) |
The seven Auto pilot events
customer.created · carrier.created · load.created · load.status · load.delivered ·
payment.customer · payment.carrier
Rules for jobs
- A job that raises a notice passes a
dedupeKey, so running it twice never raises the same notice twice. - A job that writes tenant data does so through
withOrg()and writes an audit event, like any save. - The decision ("should this alert?") is a pure function in
packages/core/jobs.tswith a test.