Skip to content

scheduler

Fresh

Source: BIBLE.md (Phase 5 Scheduling section), bible/campaigns.md (spread strategies)

core/scheduler.mjs is the universal scheduler. 6 job types, 3 spread strategies, autopilot mode.

Job types

TypeWhat
ctrCTR campaign rounds (search, click, dwell)
warmupAccount warming (generic searches plus organic clicks)
cookie-refreshRe-login to keep cookies fresh, with proxy pool fallback
rank-checkTrack SERP position for target keywords
phase-runAdvance accounts through lifecycle phases
cascadeDeploy one article to 9 platforms

Spread strategies

How runs are distributed across the day.

Gaussian curve centered on midday. Most clicks happen early to mid afternoon. Looks like real human search patterns.

 8am  ▏
 9am  ▎
10am  ▍▍
11am  ▍▍▍▍
12pm  ▍▍▍▍▍▍
 1pm  ▍▍▍▍▍▍▍▍
 2pm  ▍▍▍▍▍▍▍
 3pm  ▍▍▍▍▍▍
 4pm  ▍▍▍▍
 5pm  ▍▍▍
 6pm  ▍▍
 7pm  ▍

even

Evenly spaced with 40% random jitter. Predictable coverage, slight randomness.

burst

3 to 5 runs clustered together, then 30 to 90 minute gap. Mimics someone doing research.

Schedule fields

FieldValuesDefault
jobTypectr, warmup, rank-check, cascade, cookie-refresh, phase-runrequired
campaignIdstringoptional
runsPerDaynumber50
startHour0-238
endHour0-2322
spreadStrategynatural / even / burstnatural
daysOfWeekarray of 0-6[1,2,3,4,5]
timezonetz stringAmerica/Chicago
jobConfigobject{}

Persistence

Schedules auto-save to data/ghost-engine/schedules/. Loaded on server boot.

SIGINT handler saves scheduler state before shutdown.

Autopilot mode

Engage 3 default daemon schedules with one command:

bash
curl -X POST http://localhost:4700/schedules/autopilot \
  -H "x-api-key: ghost-engine-key"

Creates:

  1. Warmup schedule: runs account warmup batches
  2. Cookie refresh schedule: keeps cookies alive
  3. Phase runs schedule: advances accounts through phases

Disengage:

bash
curl -X DELETE http://localhost:4700/schedules/autopilot \
  -H "x-api-key: ghost-engine-key"

API routes

MethodRouteWhat
POST/schedulesCreate schedule
GET/schedulesList all
GET/schedules/:idOne schedule
DELETE/schedules/:idDelete
POST/schedules/:id/pausePause (keeps config)
POST/schedules/:id/resumeResume
POST/schedules/autopilotEngage 3 daemons
DELETE/schedules/autopilotDisengage

Example: schedule daily CTR

bash
curl -X POST http://localhost:4700/schedules \
  -H "Content-Type: application/json" \
  -H "x-api-key: ghost-engine-key" \
  -d '{
    "jobType": "ctr",
    "campaignId": "dallas-junk-removal",
    "runsPerDay": 50,
    "startHour": 8,
    "endHour": 22,
    "spreadStrategy": "natural",
    "daysOfWeek": [1, 2, 3, 4, 5],
    "timezone": "America/Chicago"
  }'

Daily click budget enforcement

The scheduler respects daily click budgets. If runsPerDay = 50, it stops at 50 even if the day is not over.

Dispatch flow

flowchart TD
  T[Tick: 60s interval] --> CH[Check all schedules]
  CH --> D{Due for run?}
  D -- no --> SK[Skip]
  D -- yes --> R[dispatchJob jobType, jobConfig]
  R --> CTR[CTR plugin]
  R --> WU[Warmup plugin]
  R --> CR[Cookie refresh plugin]
  R --> RC[Rank tracker]
  R --> PR[Phase runner]
  R --> CS[Cascade runner]

dispatchJob() in server.mjs routes jobs to the correct plugin.