I built this to monitor my team's own servers, four of them spread across the country. When a server goes down or starts throttling, running this CLI tells us which one and how bad it is.
I didn't want to use someone else's speed test app. Most of them run on TypeScript or Node.js, which means pulling in thousands of npm packages or shipping telemetry we can't audit. With Rust, every dependency is frozen in Cargo.lock, and the final product is a single executable with no runtime. It either runs or it doesn't, and nothing changes under your feet unless you rebuild it deliberately.
It still relies on Netflix's Fast.com infrastructure. I scrape fast.com, extract a token from the page's JavaScript, and hit their internal API (api.fast.com/netflix/speedtest/v2) for target server URLs and location data. If Fast.com changes their frontend, the token extraction breaks, and the tool needs an update.
This isn't designed for anybody else. It's for me and the people managing these servers with me. If it's useful to you too, great, but it's not packaged for distribution.
Once the targets are retrieved, the CLI opens N parallel connections (set with -w, defaults to 3) and starts pulling data. Every second it measures how many bytes moved in that interval (not a cumulative average from the start), converts to Mbps, and checks whether the reading has settled. If the last few readings are within 10% of each other, it stops early instead of running the full duration (configurable via --max-duration, defaults to 8 seconds). Same process for uploads, except it sends 5 MB chunks in each POST.
If no worker manages to complete a single request after three attempts each, the test aborts early instead of spinning until the duration timer expires. On a dead network this drops the wait from 8 seconds to about 3.
The terminal output is kept simple on purpose. No progress bars, no TUI spinners. Terminal rendering competes with the async runtime for CPU, and on a short speed test every cycle matters. A flickering progress bar can subtly nudge the results. These run on servers anyway -- no one needs pretty output on the servers. The colored text is just to match my terminal rendering.
If the network is down during setup or either test phase, the tool shows a red progress bar and retries up to N times (set with --max-retries, defaults to 3). After that it gives up and exits with an error. No more infinite spinning or silent 0.0 Mbps results.
The token and server list are cached to ~/.cache/speedcli/targets.json and reused for about 50 minutes. Subsequent runs start testing almost immediately instead of initiating another TCP handshake. Use --clear-cache to force a fresh fetch.
speedcli -h
Usage: speedcli [OPTIONS]
Options:
-w, --workers <WORKERS> The number of workers/threads to use to benchmark your speed (Should not be more than 5).
--clear-cache A boolean value to clear the cache used by speedcli
--max-duration <MAX_DURATION> Maximum duration in seconds for each test phase [default: 8]
--max-retries <MAX_RETRIES> Maximum retries per test phase before giving up [default: 3]
--json Output results as JSON for machine parsing
--download-only Run only the download test
--upload-only Run only the upload test
-h, --help Print help
-V, --version Print version
Output:
{
country : ZA,
approximated_city : Pretoria,
public_ip : 192.7.8.12, # realistically, it will show your public IP and not Private, this is just for demo
connected_servers : 3
}
Benchmarking Your internet speed..
Download: 142.3 Mbps
Upload: 45.1 Mbps
With --json:
{
"download": 142.3,
"upload": 45.1,
"country": "ZA",
"city": "Pretoria",
"public_ip": "192.7.8.12",
"connected_servers": 3
}The interval-based speed readings fixed the false 0.0 Mbps upload bug that appeared when using many workers on a congested server. The next step is to make the chunk size tuneable. On very fast connections, 5 MB chunks mean fewer round-trips per second, which can under-report the true speed. A dynamic chunk size that adapts mid-test should give better accuracy without adding complexity on the user's side.