Simple HTTP load testing CLI built with MoonBit
Dependencies

# macOS (Apple Silicon)
curl -sL https://github.com/paveg/molt/releases/latest/download/molt-darwin-arm64.tar.gz | tar xz
sudo mv molt /usr/local/bin/
# Linux (x86_64)
curl -sL https://github.com/paveg/molt/releases/latest/download/molt-linux-amd64.tar.gz | tar xz
sudo mv molt /usr/local/bin/git clone https://github.com/paveg/molt.git && cd molt
moon build --target native --release
cp _build/native/release/build/src/cmd/main/main.exe /usr/local/bin/molt# Start the built-in test server
molt --serve 127.0.0.1:8080 &
# Run a load test against it
molt -c 10 -d 5s http://127.0.0.1:8080/
# Or test against your own server
molt -c 50 -d 30s http://localhost:3000/api/health# Install the molt skill for your agent
npx skills add paveg/molt# Basic GET (50 connections, 10 seconds)
molt http://localhost:8080/
# POST with JSON body
molt -m POST -H 'Content-Type: application/json' \
-b '{"name":"test"}' -c 20 -d 15s http://localhost:8080/api/users
# Fixed rate: 500 RPS
molt -r 500 -c 50 -d 30s http://localhost:8080/
# Request count with suffix
molt -n 10k -c 50 http://localhost:8080/
# SLA enforcement in CI
molt --latency-threshold 100ms --error-threshold 1.0 \
-c 50 -d 30s http://localhost:8080/api/health
# JSON output for pipelines
molt --json -d 10s http://localhost:8080/ > results.json
# Warm-up: skip first 5 seconds
molt --warm-up 5s -c 20 -d 30s http://localhost:8080/
# Basic auth + custom timeout
molt --auth admin:secret -t 5s -c 10 -d 10s http://localhost:8080/api/protected
# Debug: inspect single request
molt --debug http://localhost:8080/api/healthmolt v0.1.0 -- MoonBit HTTP Load Tester
Target: http://127.0.0.1:9998/
Method: Get
Connections: 5
Duration: 3s
Running...
[0.9s] 20118 requests | 20118 req/s | p50: 0.19ms | p99: 1.05ms | errors: 0
[1.9s] 40996 requests | 20498 req/s | p50: 0.18ms | p99: 1.16ms | errors: 0
Summary:
Total requests: 57727
Successful: 57727 (100.0%)
Failed: 0 (0.0%)
Total time: 3.0s
Requests/sec: 19240.57
Latency Distribution:
p50.00 0.20ms p90.00 0.39ms
p75.00 0.30ms p95.00 0.48ms
p99.00 1.21ms p99.90 4.35ms
max 12.56ms
mean 0.25ms stdev 0.31ms
TTFB (Time to First Byte):
p50 0.19ms p99 1.20ms max 12.56ms
Status Codes:
200: 57727{
"config": {
"url": "http://127.0.0.1:9998/",
"connections": 2,
"duration_sec": 1.00,
"rate_rps": null,
"timeout_ms": 30000,
"method": "GET"
},
"summary": {
"total_requests": 19155,
"successful": 19155,
"failed": 0,
"total_time_sec": 1.00,
"requests_per_sec": 19162.76
},
"latency": {
"p50_ms": 0.09, "p75_ms": 0.10, "p90_ms": 0.13,
"p95_ms": 0.18, "p99_ms": 0.31, "p99_9_ms": 0.77,
"max_ms": 2.05, "min_ms": 0.06,
"mean_ms": 0.10, "stdev_ms": 0.06
},
"ttfb": { "p50_ms": 0.09, "p99_ms": 0.30, "max_ms": 2.04 },
"status_codes": { "200": 19155 },
"errors": {},
"custom_percentiles": { "p50.00_ms": 0.09, "p99.00_ms": 0.31 },
"throughput": { "total_bytes": 0, "bytes_per_sec": 0.00 },
"time_series": [
{ "elapsed_sec": 1.0, "requests": 19155, "rps": 19155.0, "p50_us": 91, "p99_us": 311, "errors": 0 }
]
}Usage: molt [options] <url>| Option | Short | Default | Description |
|---|---|---|---|
| Load Control | |||
| --connections | -c | 50 | Concurrent connections |
| --duration | -d | 10s | Test duration (10s, 1m, 1m30s) |
| --requests | -n | -- | Total request count, supports 10k/1m suffixes |
| --rate | -r | -- | Target requests per second |
| --warm-up | -- | Exclude initial period from stats (3s) | |
| HTTP | |||
| --method | -m | GET | HTTP method |
| --header | -H | -- | Custom header, repeatable |
| --body | -b | -- | Request body string |
| --body-file | -B | -- | Request body from file |
| --auth | -- | Basic auth (user:password) | |
| --timeout | -t | 30s | Per-request timeout |
| --redirect | -L | off | Follow 3xx redirects (up to 10 hops) |
| --disable-keepalive | off | New connection per request | |
| --insecure | -k | off | Skip TLS verification (pending upstream) |
| Output | |||
| --no-tui | off | Plain text with periodic status | |
| --json | -j | off | JSON output (implies --no-tui) |
| --csv | off | Streaming per-request CSV | |
| --percentiles | 50,75,90,95,99,99.9 | Custom percentiles | |
| CI/CD | |||
| --latency-threshold | -- | Fail if p99 exceeds value (100ms), exit 3 | |
| --error-threshold | -- | Fail if error rate exceeds % (1.0), exit 3 | |
| Other | |||
| --debug | off | Single request with full response dump | |
| --serve | -- | Start built-in test server (127.0.0.1:8080) |
graph TD
CLI["CLI Parser<br/>(argparse)"] -->|Config| COORD["Coordinator<br/>(TaskGroup)"]
COORD --> W1["Worker 1<br/>HTTP Client"]
COORD --> W2["Worker 2<br/>HTTP Client"]
COORD --> WN["Worker N<br/>HTTP Client"]
COORD -->|optional| RL["Rate Limiter<br/>(Token Bucket)"]
RL -.->|acquire| W1
RL -.->|acquire| W2
RL -.->|acquire| WN
W1 -->|RequestResult| COLL["Collector<br/>(HDR Histogram)"]
W2 -->|RequestResult| COLL
WN -->|RequestResult| COLL
COLL -->|live stats| TUI["TUI / Plain"]
COLL -->|Stats| REP["Reporter<br/>(text / JSON / CSV)"]
COLL -->|Stats| THR["Threshold<br/>Check"]| Package | Description |
|---|---|
| cmd/main | CLI parsing (cli.mbt) and orchestration (main.mbt) |
| lib/types | Config, Stats, RequestResult, HttpMethod, base64_encode |
| lib/worker | Async HTTP loop, TTFB, redirect, reconnection |
| lib/coordinator | TaskGroup orchestration, warm-up, time-series capture |
| lib/collector | Result aggregation with dual HDR Histogram (latency + TTFB) |
| lib/histogram | Gil Tene HDR Histogram (3 sig fig, 1us-1h, O(1) record) |
| lib/reporter | Text (text.mbt), JSON (json.mbt), CSV (csv.mbt), format helpers |
| lib/tui | Real-time TUI via mizchi/tui |
| lib/duration | Duration/count parser ("10s", "1m30s", "10k") |
| lib/rate_limiter | Absolute-deadline token bucket |
| lib/threshold | SLA threshold checking |
moon build --target native # build
moon build --target native --release # release build (~2.3 MB)
moon test --target native # all 241 tests
moon test # wasm-gc subset
moon fmt # format
moon check --target native # type check| molt | oha | hey | k6 | |
|---|---|---|---|---|
| Language | MoonBit | Rust | Go | Go |
| HDR Histogram | Yes (3 sig fig) | Yes | No | Yes |
| TUI | Yes | Yes | No | No |
| TTFB | Yes | Yes | No | No |
| CO correction | Yes | Yes | No | Yes |
| SLA thresholds | Yes (exit 3) | Partial | No | Yes |
| CSV | Yes (streaming) | Yes | Yes | No |
| HTTP/2 | No (planned) | Yes (+HTTP/3) | Yes | Yes |
| Scenarios | No | No | No | Yes (JS) |
| Binary | ~2.3 MB | ~3 MB | ~5 MB | ~40 MB |
Install
Download zipSimple HTTP load testing CLI built with MoonBit
Dependencies