Synthetic users.
Real traffic.

bot-runner signs bots in to your app and has them use it the way people do — writing data, uploading files, browsing all day. Set the load in one click, watch every request, and shut out any account that strays.

Load bots ONlike real users · a few visits a day, waking hours
Load bots target0 live / 0−0+01050120250500max 500 · hard cap · daily bots not counted
Ramp-up
step (bots)10every30 s
DailyBurst
Load bots target250·Ramp-up +10 / 30 sDaily
Botsspawned 0/250 · next +10 in 8s
Actions / 15 min — last 12 hours
-12hnow
GET /api/dashboardfrom198.51.100.12403 · session destroyed
1 bot account used outside prod
account · dailyceres@synthetic.testsource ip198.51.100.12
AcknowledgeDisable / purge
bot-runnernowBot account used outside prodceres@synthetic.test · 198.51.100.12 · 2 attempts
The panel

One panel. Every bot.

Set the load, watch the traffic and steer each bot from one panel, for every app plugged in. Each card plays a real piece of the panel, with a demo fleet.

Load bots target0 live / 50−50+01050120250500max 500 · hard cap · daily bots not counted
Ramp-up+10 / 30 sspawned 0/50Load paceDailyBurst
Load bots target0 live / 50−50+01050120250500max 500 · hard cap · daily bots not counted
Load and rampA target from 0 to 500 load bots, reached ten bots every 30 seconds.
Actions / 15 min — last 12 hours
actions9,381errors0.2%p50 / p9560 / 260 msbots active120
Actions / 15 min — last 12 hours
The day, by actionEvery 15 minutes stacked by action; any slice opens on its responses and bots.
saturnussaturnus@synthetic.testPaused
ceresincidentceres@synthetic.testRunning
janusjanus@synthetic.testRunning
minervaminerva@synthetic.testRunning
legionary-001legionary-001@synthetic.testRunning
The fleetDaily and load bots, filtered, searched, switched in bulk.
spending.addquiet hoursAdds a spending from the bot's own catalogue500
receipt.uploadbudgetAttaches a receipt picture to the last spending836
dashboard.viewOpens the dashboard of the current month25113
statistics.viewOpens the statistics of the current year1045
Action registryEach of the app's actions weighted and switched one by one.
Load paceEurope/Paris
awake · on the app's clock
quiet hours · spending.add
00:00now 08:0024:00
Human pacingAwake 07:00–23:00 on the app's clock, quiet hours honoured.
Hard limitsread-only · runner config
Max load bots104 / 500RUNNER_MAX_BOTS
Max requests / s7.3 / 40RUNNER_MAX_RPS
Upload budget212.8 MB / 400RUNNER_UPLOAD_BUDGET_MB · per 365 d
Hard limitsread-only · runner config
Max load bots104 / 500
Max requests / s7.3 / 40
Upload budget212.8 MB / 400
Hard limits500 bots, 40 requests a second, 400 MB of uploads a year — fixed at boot.
1 bot account used outside prod
account · dailyceres@synthetic.testsource ip198.51.100.12
AcknowledgeDisable / purge
bot-runnernowBot account used outside prodceres@synthetic.test · 198.51.100.12 · 2 attempts
1 bot account used outside prod
account · dailyceres@synthetic.testsource ip198.51.100.12
AcknowledgeDisable / purge
Incidents and pushA bot account seen off its address raises a banner and a push to your phone.
01 — Load

Dial the load in one click

Pick a target from 0 to 500 load bots: the runner ramps up ten bots every 30 seconds, and down all at once. Run them at a human pace, a few visits a day, or in bursts for a stress test.

  • Presets from 10 to 500 bots
  • Daily pace, or burst for stress tests
  • Quiet hours in the app's own time zone
Load bots ON
Load bots target0 live / 0−0+01050120250500max 500 · hard cap · daily bots not counted
Ramp-up+10 / 30 sspawned 0/250
Populationlive · target
load bots0 / 2500% of target
daily bots4 / 4permanent
02 — Observe

Read every request on one clock

Each 15 minutes of the day is stacked by action. Open a slice to see its requests, its errors, its p50 and p95 latency, its response codes and the bots that made them.

  • Latency per action and per bot
  • 2xx, 4xx and 5xx at a glance
  • Each bot's last requests in a drawer
Actions / 15 min — last 12 hours
-12hspending.addreceipt.uploaddashboard.viewstatistics.viewnow
Bot activityMon 12 Oct · 12:00 – 12:15
actions9,381errors18 · 0.2%p50 / p9560 / 260 ms
by action
spending.add5,046receipt.upload806dashboard.view2,521statistics.view1,008
responses
2xx 9,3814xx 75xx 11
legionary-001load146legionary-002load143legionary-003load137legionary-004load131
03 — Contain

Lock every bot to its address

Bot accounts are flagged as synthetic and allowed from known addresses only. One seen anywhere else is refused with a 403, its session destroyed, and the incident reaches every page and your phone.

  • A guard on every request of the app
  • Web Push to every subscribed phone
  • Disable or purge in one click
GET /api/dashboardfrom198.51.100.12403 · session destroyedSYNTHETIC_ALLOWED_IPS = 203.0.113.10 · ceres@synthetic.test
1 bot account used outside prodsynthetic-foreign-ip · SyntheticIpGuard → 403 + session destroyed
account · dailyceres@synthetic.testsource ip198.51.100.12Mozilla/5.0 (iPhone; CPU iPhone OS 18_0)attempts2last seen15:32:00just now · push ✓
AcknowledgeDisable / purge
Incident historyECS logs · synthetic-foreign-ip
15:32:00ceres@synthetic.test198.51.100.122open
12/10, 15:55legionary-042@synthetic.test203.0.113.477purged
10/10, 15:58legionary-009@synthetic.test192.0.2.33disabled
bot-runnernowBot account used outside prodceres@synthetic.test · 198.51.100.12 · 2 attempts
Preview

The whole fleet, one panel.

Four screens of the panel, playing with a demo fleet: the overview, the bots, the metrics and the incidents.

Demo fleet · sample data
And the rest

Built to plug into any app.

Each app plugs in through one adapter behind a single contract: its sign-in, its actions, its colours. Actions are discovered from a folder, so teaching the bots a new feature is one file.

Inside an app, the whole panel takes on that app's palette, so you always know whose traffic you are steering. Outside, it wears its own neutral terminal look.

Bots come in through the same doors as a browser: the session cookie, a CSRF header on every write, a fresh sign-in a minute before the session expires, and one retry on a 401.

Visits arrive with exponential gaps, awake 07:00 to 23:00 on the app's own clock, the wake-up corrected on daylight-saving nights.

Safety

Real traffic.
Never a real risk.

bot-runner writes to production, so it is built to stay inside its lines: accounts usable only from known addresses, limits fixed at boot, and a control channel that never leaves the machine.

IP-locked accountsEvery bot account is flagged synthetic and refused, its session destroyed, from any address off its allowlist.
Caps fixed at boot500 bots, 40 requests a second through one token bucket, 400 MB of uploads a year.
Loopback controlThe panel drives each runner over loopback only, behind a token compared in constant time.
Every change auditedThe panel's account is sealed and rate-limited, and every change made to a runner is recorded.

Synthetic users.
Real traffic.

Realistic data and steady load for every app you ship, from one panel.