Appearance
scheduler
FreshSource: 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
| Type | What |
|---|---|
ctr | CTR campaign rounds (search, click, dwell) |
warmup | Account warming (generic searches plus organic clicks) |
cookie-refresh | Re-login to keep cookies fresh, with proxy pool fallback |
rank-check | Track SERP position for target keywords |
phase-run | Advance accounts through lifecycle phases |
cascade | Deploy one article to 9 platforms |
Spread strategies
How runs are distributed across the day.
natural (recommended)
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
| Field | Values | Default |
|---|---|---|
jobType | ctr, warmup, rank-check, cascade, cookie-refresh, phase-run | required |
campaignId | string | optional |
runsPerDay | number | 50 |
startHour | 0-23 | 8 |
endHour | 0-23 | 22 |
spreadStrategy | natural / even / burst | natural |
daysOfWeek | array of 0-6 | [1,2,3,4,5] |
timezone | tz string | America/Chicago |
jobConfig | object | {} |
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:
- Warmup schedule: runs account warmup batches
- Cookie refresh schedule: keeps cookies alive
- 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
| Method | Route | What |
|---|---|---|
| POST | /schedules | Create schedule |
| GET | /schedules | List all |
| GET | /schedules/:id | One schedule |
| DELETE | /schedules/:id | Delete |
| POST | /schedules/:id/pause | Pause (keeps config) |
| POST | /schedules/:id/resume | Resume |
| POST | /schedules/autopilot | Engage 3 daemons |
| DELETE | /schedules/autopilot | Disengage |
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.