Skip to content

tonhe/viaduct

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

via

A modern terminal-native traceroute replacement with real-time ECMP multipath discovery

Go License: MIT GitHub release macOS Linux Windows

Traditional traceroute shows one path. Real networks aren't single paths — they're forests of parallel ECMP links, rate-limiting routers, and load balancers that return different answers every probe. via sees all of it.

via replaces traceroute, mtr, and paris-traceroute with a live-updating TUI that shows packet loss, latency stats, reverse DNS, ASN enrichment, and all parallel paths through load-balanced networks.

via showing ECMP multipath trace with sparklines and ASN enrichment

Install · Quick Start · Features · Display Modes · Themes · Flags


How via Compares

Feature traceroute mtr paris-traceroute via
ECMP multipath discovery Yes Yes
Live-updating TUI Yes Yes
ASN enrichment Yes
Rate-limit detection Yes
TCP/UDP/ICMP probes Partial ICMP UDP/ICMP All + Auto
Display modes & sparklines 4 modes
Destination alerting Yes
Color themes 61
Settings persistence Yes

Install

Homebrew

brew install tonhe/tap/viaduct

From source

git clone https://github.com/tonhe/viaduct.git
cd viaduct
make install    # builds and copies to ~/bin/

Note

Linux users can avoid sudo by granting raw socket capability once:

sudo setcap cap_net_raw+ep $(which via)

Quick Start

# Trace with ECMP multipath discovery (default: UDP, 6 flows)
via google.com

# TCP SYN probes — passes firewalls that block UDP/ICMP
via -P tcp google.com

# Auto mode — starts UDP, falls back to ICMP if no responses
via -P auto google.com

# Non-interactive report for scripts
via -r google.com

Tip

Press Esc during any trace to open the settings menu — change themes, probe settings, or alert thresholds without restarting.


Features

  • ECMP multipath — Paris-traceroute-style probing reveals all parallel paths through load-balanced networks with tree-view divergence (├──/└── connectors)
  • Automatic rate-limit detection — detects routers that rate-limit ICMP TTL Exceeded and silently switches to direct ping, replacing phantom loss with real data ( marker)
  • 4 display modes — cycle with d: Default, Health, Latency, Variability — each with sparklines, trend detection, and hop-to-hop deltas
  • Destination alerting — monitors loss% and latency thresholds; identifies the responsible hop and ASN when alerts fire
  • ASN enrichment — each hop shows its AS number with boundary transitions highlighted; full org name at wide widths
  • 3 probe protocols + auto — UDP (ECMP), TCP SYN (firewall bypass), ICMP (classic), and Auto (UDP with ICMP fallback)
  • 61 color themes — Solarized Dark default; switch with --theme or the settings menu
  • Config persistence — settings saved to ~/.config/via/config.toml; CLI flags override at runtime

Protocol Modes

Protocol Flag ECMP Dest Detection Best For
UDP -P udp (default) Yes (src-port variation) ICMP Port Unreachable General use, ECMP discovery
TCP SYN -P tcp Yes (src-port variation) SYN-ACK or RST Targets behind firewalls
ICMP -P icmp No (single-path) Echo Reply Simple traces, compatibility
Auto -P auto Yes → No on fallback Per active protocol Unknown networks

Auto mode starts with UDP for ECMP discovery. If no responses are received after 3 rounds (~3 seconds), it automatically falls back to ICMP. The TUI header updates to reflect the active protocol.


IPv6 Support

via runs over IPv4 or IPv6, selecting the family automatically based on the target's DNS records and the host's transport capability. Force a specific family with -4 or -6:

via google.com          # auto-selects (prefers IPv6 if available)
via -4 google.com       # IPv4
via -6 google.com       # IPv6

If -6 is requested and the target has no AAAA record (or the host has no IPv6 transport), via exits with a clear error rather than silently falling back.


Rate-Limit Detection & Ping Supplement

Many routers rate-limit ICMP TTL Exceeded responses, causing tools like mtr and traceroute to show phantom packet loss at intermediate hops. This is the single most common source of misdiagnosis in traceroute output — operators see 60% loss at hop 3 and assume a problem, when the router is simply deprioritizing TTL Exceeded replies.

via automagically detects this. After a few probe rounds, it identifies hops where loss is high but all downstream hops are healthy — the hallmark of rate-limiting, not real packet loss. It then launches direct ICMP Echo Requests (pings) to those hops in the background, on a separate socket, and replaces the misleading TTL Exceeded stats with accurate ping-derived data.

The dagger on the Snt column indicates a hop whose stats come from direct ping rather than TTL Exceeded replies. No configuration needed — it just works. Disable with --no-ping if you prefer raw TTL Exceeded data.


Display Modes

Press d to cycle through four view presets:

Mode Columns Best For
Default Loss%, Snt, Avg, Best, Wrst, StDev, Last General overview (mtr-equivalent)
Health Loss%, Avg, Delta, Sparkline, Trend Quick health assessment
Latency Avg, Best, Wrst, GMean, Delta, Sparkline Detailed latency analysis
Variability StDev, Jitter, Jitter Mean, Sparkline, Trend Stability analysis

Additional metrics:

  • Delta — hop-to-hop latency difference; the largest delta is highlighted in amber to pinpoint where delay is introduced
  • GMean — geometric mean, a better central tendency for skewed latency distributions
  • Jitter / Javg — current jitter (|rtt - prevRTT|) and running mean jitter
  • Sparkline — heat-strip mini chart (green→gold→red gradient) showing per-round average latency (28 rounds); lost probes render as dim ·
  • Trend — sliding-window linear regression over 10 rounds; shows ↑ degrading, ↓ improving, or ~ stable

Destination Alerting

via monitors the destination hop for sustained degradation and alerts when thresholds are exceeded. By default, alerts fire when loss exceeds 5% for 3 consecutive rounds.

When an alert fires, via walks backward through the hops to identify which hop and ASN is likely causing the problem, and displays the alert in a red bar above the status line.

Configure with --alert-loss, --alert-latency, and --alert-rounds. Disable with --no-alert. Press r to reset alert state.


Themes

via ships with 61 built-in color themes. Set a theme with --theme <slug> or choose one in the settings menu (Esc).

Show all 61 themes
Family Themes
Solarized solarized-dark (default), solarized-light, solarized-dark-hc, solarized-osaka-night, solarized-darcula
Catppuccin catppuccin-mocha, catppuccin-macchiato, catppuccin-frappe, catppuccin-latte
Gruvbox gruvbox-dark, gruvbox-light, gruvbox-dark-hard, gruvbox-material
Tokyo Night tokyo-night, tokyo-night-storm, tokyo-night-moon, tokyo-night-day
One/Atom one-dark, one-light, one-half-dark, one-half-light
Rosé Pine rose-pine, rose-pine-moon, rose-pine-dawn
Kanagawa kanagawa-wave, kanagawa-dragon, kanagawa-lotus
Everforest everforest-dark, everforest-light
Fox nightfox, carbonfox, nordfox, dawnfox
Monokai monokai-classic, monokai-pro, monokai-vivid
GitHub github-dark, github-dark-dimmed, github-light
Standalone dracula, nord, flexoki-dark, flexoki-light, cyberdream, ayu-mirage, ayu-dark, ayu-light, challenger-deep, night-owl, night-owlish-light, doom-one, moonfly, sonokai, oxocarbon, poimandres, vesper, palenight, horizon, zenburn, modus-operandi, tomorrow

Usage

# Basic trace with ECMP multipath (default: UDP, 6 flows)
via google.com

# Use TCP SYN probes (port 443 default, passes firewalls)
via -P tcp google.com

# Use ICMP probes (single-path, like classic traceroute)
via -P icmp google.com

# Auto mode: starts UDP, falls back to ICMP if no responses
via -P auto google.com

# TCP with custom port
via -P tcp -p 80 example.com

# Trace with custom hop limit
via --max-hops 15 8.8.8.8

# Set per-probe timeout and interval
via --timeout 2s -i 500ms cloudflare.com

# Probe 12 ECMP flows
via --paths 12 google.com

# Disable ECMP, single-path mode
via --no-paths cloudflare.com

# Report mode (non-interactive, for scripts)
via -r google.com

# Report mode with 5 rounds, no DNS
via -r -c 5 -n 8.8.8.8

# Disable ASN lookups
via --no-asn google.com

# Disable ping supplement
via --no-ping google.com

# Set alert thresholds (alert on >10% loss sustained for 5 rounds)
via --alert-loss 10 --alert-rounds 5 google.com

# Alert on sustained latency above 200ms
via --alert-latency 200ms google.com

# Disable alerting entirely
via --no-alert google.com

Flags

Flag Short Default Description
--protocol -P udp Probe protocol: udp, icmp, tcp, auto
--port -p (protocol) Destination port (33434 for UDP, 443 for TCP)
--max-hops 30 Maximum number of hops to probe
--timeout 1s Per-probe response timeout
--interval -i 1s Probe round interval
--count -c 0 Number of probe rounds (0 = infinite)
--first-ttl -f 1 Starting TTL
--psize -s 64 Payload size in bytes
--paths 6 Number of ECMP flows to probe
--no-paths Disable ECMP, single-path mode
--report -r Report mode: non-interactive, print table and exit
--no-dns -n Skip reverse DNS lookups
--no-asn Skip ASN lookups
--no-ping Skip ping supplement for rate-limited hops
--alert-loss 5.0 Loss% threshold for destination alert (0 = disabled)
--alert-latency 0 Latency threshold for destination alert (0 = disabled)
--alert-rounds 3 Consecutive bad rounds before alert fires
--no-alert Disable alerting
--theme (config) Color theme slug (e.g. 'dracula', 'nord')
--version Print version and exit

Keyboard Shortcuts

Key Action
j / k Scroll up/down
g / G Jump to top/bottom
p Pause/resume probing
r Reset all hop stats and ping supplement
d Cycle display modes (Default → Health → Latency → Variability)
q Quit (prints final summary)
Esc Open settings menu

Architecture

via is built on a fully concurrent architecture that keeps every subsystem independent:

  • Protocol-agnostic probe engine runs in its own goroutine behind a ProbeProtocol interface, firing packets without waiting for DNS or the UI
  • DNS resolver uses a worker pool (4 goroutines) with dedup and caching — lookups never block probes
  • ASN enricher uses the same worker pool pattern to look up AS numbers via Team Cymru DNS — zero config, no API keys
  • TUI renders independently on a 100ms tick, reading from thread-safe shared state
  • No serialized I/O — unlike mtr (single-threaded C with synchronous DNS), every operation runs concurrently

The result: probes keep firing at full speed while hostnames resolve in the background and the UI stays smooth.

Testing

via runs a cross-platform test matrix on every push to main and feat/* branches (see .github/workflows/test.yml).

Locally:

make test          # go test ./...  (fast, no race detector)
make test-race     # go test -race ./...
make test-tui      # TUI package only
make coverage-gate # enforce per-package coverage floors

Regenerating golden files after intentional TUI output changes:

make golden-update

Then review the diff to internal/tui/testdata/**/*.golden and commit alongside the code change.

Soak tests (opt-in)

Long-running live-network sweep to catch environmental, timing, and leak bugs. NEVER runs on CI. Requires make install first plus sudo or CAP_NET_RAW on ~/bin/via.

make soak-test

Runs 10+ trace scenarios (v4/v6/mixed, UDP/TCP/ICMP/auto) against public hosts, tracks goroutine count and heap growth, and reports any suspicious patterns. ~30 minute runtime.

CI matrix: Ubuntu, macOS, and Windows against Go 1.25.6. Build, race, coverage, and per-package coverage floors all run on each OS.

License

MIT

About

A modern, terminal-native traceroute tool with real-time ECMP multipath discovery.

Topics

Resources

License

Stars

22 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages