loadforge
v1.0.0
Published
Pre-launch load & stress self-test toolkit for your own sites. Realistic browser-like traffic, parallel full-load engine, cross-platform single executable.
Maintainers
Readme
loadforge
Pre-launch load & stress self-test toolkit for your own websites and APIs.
Point it at infrastructure you own or are authorized to test, crank up parallel full-load traffic with realistic browser signatures, and find out whether your service holds up before real users do.
⚠️ Ethical use only.
loadforgeis built for testing your own infrastructure. Do not aim it at third-party sites. Running against a public host is blocked unless you explicitly pass--i-own-this-targetto confirm ownership/authorization.The tool supports application-layer client-IP spoofing (
--spoof-ip) so your target app sees a randomized/fake client IP — useful to validate your own rate-limit, geo, and multi-tenant logic. This only setsX-Forwarded-For/X-Real-IPheaders; it does not hide the real TCP source IP, and the tool provides no network-layer IP spoofing, botnets, or amplification.
Features
- Node ≥ 24, zero runtime dependencies (native
fetch+child_process). - Parallel full-load engine — one process per CPU core, each driving many
concurrent requests. Use
--maxto saturate the machine. - Realistic browser fingerprinting — rotating User-Agents,
sec-ch-ua,sec-fetch-*,accept-language, etc., so traffic looks like genuine users (great for validating your own anti-bot / rate-limit defenses). - Application-layer client-IP spoofing (
--spoof-ip) — advertise a per-request randomizedX-Forwarded-For/X-Real-IP/X-Client-IPso your target app sees distinct fake client IPs. Defaults topool(on by default — a plain run already hides the real client IP). Modes:random(fresh IP each request),pool(random pick from a stable N-IP pool),off(disabled), or a fixed<ip>. Optional--spoof-ipv6for IPv6. Great for testing your own rate-limit / geo / multi-tenant logic. App-layer only — never changes the real TCP source IP. - Real-browser mode (
--browser) — optional headless Chromium via Playwright for the most authentic request shape. - Configurable attack profile — concurrency, total RPS, duration, ramp-up, HTTP method, payload, custom headers.
- Single executable — build a cross-platform binary via Node's official Single Executable Applications (SEA) feature; also published to npm.
- Dual package (ESM + CJS) — import it from modern ESM projects or legacy CommonJS projects alike (see below).
- One-click release —
npm run releasedrives code review → commit → interactive version pick → tag → changelog → push to both remotes → npm publish, with live progress. - Automated changelog & code review — every push regenerates the changelog and runs the review gate in CI; every release shows its changes on GitHub.
Install
npm install -g loadforge
# or run directly
npx loadforge -t http://localhost:8080 -d 30sFor real-browser mode:
npm i playwright && npx playwright install chromiumUse as a library (ESM or CJS)
// ESM
import {
buildHeaders,
PROFILES,
requiresOwnTargetConfirmation,
randomClientIp,
isIpLiteral,
} from 'loadforge';
// CommonJS
const {
buildHeaders,
PROFILES,
requiresOwnTargetConfirmation,
randomClientIp,
isIpLiteral,
} = require('loadforge');
// build a realistic header set advertised as a random client IP (app layer only)
const headers = buildHeaders({ 'X-Tenant': 'self-test' }, false, randomClientIp());
console.log(PROFILES.extreme);
console.log(isIpLiteral('203.0.113.7')); // trueBuild from source
Requires Node ≥ 24.
npm ci
npm run build # -> dist/cli.bundle.cjs + dist/loadforge-<os>-<arch> (SEA exe)
./dist/loadforge-linux-x64 --helpLibrary-only bundles (no executable) can be built separately:
npm run build:lib # -> dist/index.mjs + dist/index.cjs
npm run build:cli # -> dist/cli.bundle.cjs (CLI entry for the npm package)Usage
# self-test your local service with the "normal" preset
loadforge -t http://localhost:8080 -d 30s --profile normal
# full-load stress test across all CPU cores
loadforge -t http://localhost:3000 --max --profile extreme
# controlled ramp-up against staging (public host needs confirmation)
loadforge -t https://staging.mysite.com -d 2m -c 200 -r 5000 --ramp-up 15 --i-own-this-target
# real headless-browser traffic
loadforge -t http://localhost:8080 --browser -c 8 -d 1m
# API load test with POST + report
loadforge -t http://localhost:8080/api -m POST --payload '{"x":1}' --json -d 30s --report json
# hide the real client IP from the app layer: random IP per request
loadforge -t http://localhost:8080 -d 30s --spoof-ip random
# simulate N distinct clients via a stable IP pool (e.g. rate-limit testing)
loadforge -t http://localhost:8080 -d 30s --spoof-ip pool --spoof-ip-pool 500
# force a specific client IP (e.g. to test geo / allow-list logic)
loadforge -t http://localhost:8080 -d 30s --spoof-ip 203.0.113.45⚠️
--spoof-ipis on by default (pool mode). It only changes the IP your application reads from request headers — the server's connection / firewall logs still record your real source IP. It is not a proxy / VPN substitute. Pass--spoof-ip offto disable.
Parameters
| Flag | Default | Description |
|---|---|---|
| -t, --target | — | Required. Target URL |
| -w, --workers | CPU count | Parallel processes |
| --max | false | Use all CPU cores |
| -c, --concurrency | profile | Concurrent in-flight requests per worker |
| -r, --rate | profile | Target RPS total (0 = unlimited / full load) |
| -d, --duration | profile | 30s, 5m, 1h |
| --ramp-up | profile | Scale load up over N seconds |
| --profile | normal | light | normal | stress | extreme |
| -m, --method | GET | HTTP method |
| --payload | — | Request body |
| --headers | — | Key: Value, repeatable |
| --json | false | JSON Accept header (APIs) |
| --browser | false | Real headless Chromium (Playwright) |
| --report | — | json | csv report file |
| --i-own-this-target | false | Confirm ownership for public hosts |
| --spoof-ip | pool | App-layer client IP: pool (default) | random | off | <ip> |
| --spoof-ipv6 | false | Generate IPv6 client addresses in random/pool mode |
| --spoof-ip-pool | 1024 | Pool size for pool mode (min 2) |
| --dry-run | false | Print resolved config, send no traffic |
| -h, --help | — | Help |
One-click release (npm run release)
The maintainer runs this locally. It walks through every release step and shows live progress, aborting on any failure:
[1/6] Code review (abort on failure)............ runs `scripts/review.js`
[2/6] Checking working tree...................... if dirty, prompts for a commit message
[3/6] Selecting version.......................... interactive ↑/↓ (default patch),
shows current → target
[4/6] Generating changelog....................... updates CHANGELOG.md
[5/6] Tagging vX.Y.Z............................. git tag -a vX.Y.Z
[6/6] Push & publish............................. push origin(github), then npm publishFlags (for CI / automation / dry runs):
npm run release -- --dry-run # show the full plan, run nothing destructive
npm run release -- --bump minor # skip the interactive selector
npm run release -- --yes # accept the default (patch) without promptingInteractive selector example:
Select release version (↑/↓ move · Enter confirm · Ctrl+C cancel)
current: 0.1.0
> patch → 0.1.1
minor → 0.2.0
major → 1.0.0
prerelease → 0.1.1-pre.0Repository remotes
This project is hosted on GitHub:
origin -> https://github.com/GuoSirius/loadforge.git (github)npm run release pushes the tag and code to the github remote, triggering
the GitHub Actions release workflow (cross-platform binaries + npm publish).
CI & Release automation
- CI (
ci.yml) — on every push tomain, runs the automated code review (static checks + test suite) and regeneratesCHANGELOG.md, committing it back with[skip ci]. - Release (
release.yml) — on av*tag, builds the SEA executables for Linux x64/arm64, macOS arm64 and Windows x64, attaches them to the GitHub release (with this release's changelog as the notes), and publishes the package to npm. RequiresNPM_TOKENandGITHUB_TOKENsecrets.
License
MIT
