Posted Jun 3, 2026
pingtrace from npm to Go
pingtrace is a single static Go binary that runs ping, traceroute and live MTR in one command - no runtime required - with DNS, ipinfo.io, and PeeringDB enrichment built in.
A network CLI is also a distribution problem: why I rewrote pingtrace from npm to Go
The first version validated the workflow. The rewrite addressed where troubleshooting software must actually run.
When I started building pingtrace, I was not trying to replace ping or traceroute.
I was trying to remove a small but recurring friction from network troubleshooting: I run a probe, run a trace, look up hosts, try to understand which network or provider I am traversing, and then collect output for someone else to read later.
That workflow is familiar to anyone who has investigated a path problem, a routing anomaly, an intermittent timeout or simply needed a fast first look at a target.
The first implementation of pingtrace was published as an npm CLI. It combined ping and traceroute, accepted multiple inputs, enriched results, and offered export. It did its job: it proved the idea had value.
Then the project met a more important question:
What should installation look like for a tool that is most useful when the network is already a problem?
That question is why pingtrace 1.0 is now written in Go and distributed as standalone binaries.
The original npm version was not a mistake
It is tempting, after a rewrite, to describe the first implementation as the wrong one. I do not see it that way.
A TypeScript/npm implementation was an efficient way to explore the interaction model. It helped answer practical questions:
- Should a single command run both ping and trace?
- Is output more useful when enriched with DNS, organisation, ASN or peering information?
- Does CIDR or CSV input make the tool useful beyond one-off tests?
- Should the output be exportable rather than trapped in a terminal session?
The npm version established a strong product direction: the value of pingtrace is not that it invents new network probes. The value is that it makes ordinary, trusted probes faster to run, easier to interpret and easier to preserve.
The architecture only became an issue after the workflow was proven.
The installation mismatch
A developer-oriented CLI can reasonably assume a development runtime exists. A troubleshooting utility has a different life.
It may need to run on:
- a jump host;
- a workstation where installing global packages is discouraged;
- a fresh diagnostic virtual machine;
- a macOS laptop during an incident;
- a Windows endpoint where the operator simply needs a zip and an executable;
- an automation environment where one self-contained artifact is easier to distribute and verify.
In those situations, telling a user to install Node.js and then globally install an npm package introduces a dependency before the diagnostic work even begins.
That does not make npm wrong. It means the distribution model was not aligned with the environment in which I wanted pingtrace to be used.
Why Go matched the tool
The move to Go was driven by three product requirements, not by language fashion.
A portable executable is a feature
The current release is available as pre-built artifacts for Linux, macOS and Windows, with checksums on GitHub Releases. A user can download the relevant package or archive and run the tool without installing a language runtime first.
That is not merely packaging polish. For operational utilities, deployment friction directly affects whether the tool gets tried at all.
Concurrency matters when one target becomes a network range
A quick probe against 8.8.8.8 is one thing. A CSV list or an IPv4 CIDR block is another.
The Go rewrite introduced bulk execution with a worker pool, progress output and structured exports. This makes pingtrace useful for targeted range checks without converting the terminal into hundreds of unreadable scrolling tables.
The underlying logic remains conservative: pingtrace invokes the operating system’s native probe commands and focuses on orchestration, parsing and presentation.
A terminal-native UI makes live diagnostics readable
A troubleshooting command is not complete when it prints the final line. Users need reassurance while work is happening and clarity as results arrive.
The Go version uses the Charmbracelet stack to provide:
- streaming ping replies and traceroute hops;
- a live MTR mode;
- a configuration TUI;
- responsive terminal tables;
- clear progress during bulk operations.
This is not decoration for its own sake. When a trace is slow, a hop times out, or a large range is running, the interface tells the operator that the process is alive and what it has discovered so far.
The principle I refused to change
A rewrite can easily become an excuse to reimplement everything. I intentionally did not do that.
pingtrace still delegates probing to the operating system:
pingplustracerouteortracepathon Unix-like systems;pingplustracerton Windows.
It parses what those tools return, enriches reachable addresses with optional context, and exports results for later analysis.
This matters because network engineers already understand the behaviour and permissions of the native tools on their platforms. pingtrace should make that workflow clearer, not introduce a mysterious probe engine whose results must be interpreted differently.
From terminal output to useful evidence
The Go release expands the original idea in several practical directions:
- Live MTR for a continuously updated path view.
- Inputs ranging from one target to comma-separated targets, CSV files and CIDR ranges.
- Split-horizon DNS handling for internal and public addresses.
- Optional ipinfo.io and PeeringDB context for public network hops.
- CSV output for analysis and JSON output validated against a schema.
- Cross-platform parsing verified across Linux, macOS and Windows.
The current v1.0.1 release also fixes an issue that matters precisely because exports are supposed to become evidence: enrichment fields are correctly written into CSV and JSON reports, and private addresses are routed to the private-DNS field.
A terminal table is useful in the moment. A correct structured report is what makes a troubleshooting session repeatable, reviewable and suitable for automation.
Installation should reflect the purpose
For users, the most direct path is a pre-built release asset:
Latest release:
https://github.com/skhell/pingtrace/releases/latest
For Go developers who already have the toolchain installed:
go install github.com/skhell/pingtrace/cmd/pingtrace@latest
A first run can be as simple as:
pingtrace 8.8.8.8
Or, when the path needs ongoing observation:
pingtrace 1.1.1.1 --mtr
Or, when the output must be kept:
pingtrace 8.8.8.8 --export ./reports --json
The larger lesson
The most valuable outcome of the rewrite was not learning that one language is universally better than another. It was learning to treat distribution as part of product architecture.
For infrastructure and network tools, the user may be operating under pressure. They may be restricted by endpoint policy. They may be inside a temporary environment. They may need to preserve output as evidence. They may be comparing paths across many addresses rather than experimenting locally.
In that setting, a runtime-free executable, explicit versioned releases, checksums, structured output and native terminal behaviour are not secondary details.
They are the tool.
The npm version proved that combining probes and enrichment was useful. The Go release makes pingtrace easier to take into the environments for which it was built.
The project is open source:
- GitHub repository: https://github.com/skhell/pingtrace
- Latest releases: https://github.com/skhell/pingtrace/releases/latest
- Issue tracker: https://github.com/skhell/pingtrace/issues
Feedback from network engineers, SREs and infrastructure practitioners is very welcome.