network-doctor

command module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 2, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

README

network-doctor

A terminal UI that diagnoses your network connectivity and tells you where the connection breaks in plain English — not just a wall of tool output.

The home screen runs short, native, rootless probes as a small dependency graph, then a diagnosis engine turns their combined state into a one-line verdict. The left pane is the probe chain; the right pane is the diagnosis plus details for the selected probe.

Network Doctor                Diagnosis

✓ Interface
✓ Internet (TCP egress)
✓ DNS github.com              HTTPS github.com
✓ TCP github.com:443          PASS — HTTPS 200 (responded)
✓ TLS github.com
✓ HTTP github.com
✓ HTTPS github.com

↑/↓ select · r rerun · q quit

How it diagnoses

Probes form a dependency graph with independent branches, so an unrelated failure never hides a working one:

  • Direct-egress path (independent of DNS): Interface → Internet (TCP egress). Always runs, so "DNS is down but the internet is up" is diagnosable.
  • Plain HTTP path: Interface → DNS → HTTP :80.
  • Selected target path: Interface → DNS → TCP → TLS → HTTPS for secure web targets, or the applicable protocol row for other ports.

Each row is one of four states: ✓ Pass, ✗ Fail, ⊘ Skip (a prerequisite failed), or – N/A (doesn't apply — e.g. DNS on an IP literal).

Probe Passes when Notes
Interface A non-loopback interface is up and running
Internet (TCP egress) A TCP connect to 1.1.1.1/8.8.8.8:443 succeeds honestly "direct egress" — proxy-only networks can fail this
DNS The host resolves to an IPv4 (system resolution) IP-literal targets are N/A; all A records are retained
TCP A TCP connect to the target port succeeds tries each A record, pins the first that connects
TLS The TLS handshake (SNI + cert verification) succeeds bad/expired cert, clock skew, or MITM → Fail
HTTP Port 80 returns any HTTP response (incl. 3xx/4xx/5xx) Independent HEAD after DNS, redirects off, proxy off
HTTPS The selected TLS port returns any HTTP response HEAD against the TLS-validated IP, redirects off, proxy off
SSH/SMTP banner TCP connects (banner read best-effort) bounded read; "connected but silent" still passes

RTT is measured from the TCP-connect handshake (no ICMP, no root). The source IP and interface are read from the winning connection's LocalAddr, with a UDP-connect fallback (sends no packets) for path identity on failure. Every probe is IPv4-only and bounded by a 4-second timeout.

Install

Runs on Linux, macOS, and Windows.

Download a prebuilt binary from the latest release, or install with Go 1.26+:

go install github.com/mplaczek99/network-doctor@latest

Check what you're running with network-doctor --version.

Or build from a clone:

git clone https://github.com/mplaczek99/network-doctor
cd network-doctor
go build -o network-doctor .

Usage

network-doctor                  # generic local + internet diagnosis
network-doctor github.com       # diagnose the path to a host (→ HTTP + TLS + HTTPS)
network-doctor github.com:22    # port selects the protocol rows (→ SSH banner)
network-doctor https://host:80  # explicit scheme selects the protocol (→ TLS + HTTPS on :80)

The target parser has two independent axes: the port (explicit :port > scheme default > 443) and the protocol rows (an explicit http/https scheme wins; otherwise inferred from the port — 443/8443→HTTP+TLS+HTTPS, 80→HTTP, 22→SSH, 25/587→SMTP). Hosts are validated against a strict allowlist; IPv6 literals are rejected (IPv4 only).

Key Action
/ (k/j) select a probe row
r rerun the chain
q / Ctrl-C quit

Drill-down tools

Each row in the diagnosis is evidence; when you want proof, run a real tool as a cancellable streaming job (one at a time). The contextual toolbox shows the tools available for the current target with their hotkeys — missing binaries are greyed out with an install hint. Output is bounded, sanitized (no terminal-escape injection from a hostile server), and a few stable facts are extracted on completion.

The same hotkeys map to each OS's built-in tools:

Key Linux macOS Windows
i ip route netstat -rn route print -4
s ss -tunp netstat -an -p tcp netstat -ano
p ping -c 4 -W 2 ping -c 4 ping -n 4 -w 2000
d dig +time=2 +tries=1 dig +time=2 +tries=1 nslookup
c curl … -w '…' (locale-proof facts) same curl.exe (bypasses the PowerShell 5.1 curl alias)
t traceroute -w 2 -q 1 -m 20 same tracert -w 2000 -h 20
m mtr --report --report-cycles 5 same (via brew) pathping -h 20 -q 5 -p 100 -w 500 (own 90 s budget)

The routes/sockets tools are target-independent; the rest need a host. Tools are run with an argument slice (never a shell string), in their own process group on Unix (cancel kills descendants too), unprivileged — on a permission error you get the command to re-run with sudo, never an auto-escalation. The displayed command is copy-pasteable in a POSIX shell (Linux/macOS) or PowerShell (Windows; cmd.exe paste is not supported).

--toolbox [<host>] opens straight into the toolbox without auto-running the chain (press r to run it). With no host, only the target-independent tools are offered.

Exit codes
Situation Exit
Chain completed, no failed row (Skips allowed) 0
Any failed row 1
Quit before the chain finished 1
Bad arguments / validation reject 2
network-doctor github.com || echo "path to github is broken"

Platform support

All probes, the diagnosis engine, and the TUI are pure Go and identical on Linux, macOS, and Windows. The platform-specific garnish (default gateway, Wi-Fi SSID) uses the kernel directly on Linux (/proc/net/route, wireless ioctl) and the OS's built-in commands elsewhere (route/networksetup on macOS, route print/netsh wlan on Windows); when those fail the fields degrade to empty rather than failing a probe.

Windows localization caveat: console tools emit the OEM code page, so non-ASCII localized text in raw tool output shows as visible ? replacement characters, and ping fact extraction (% loss, Average) works on English Windows only. Everything load-bearing (route table cells, the untranslated SSID label, nslookup addresses) is parsed locale-independently.

Roadmap

Implemented: native DAG probes + diagnosis engine + two-pane UI (Phase 1), cancellable streaming tool jobs with ping/dig/curl (Phase 2), and traceroute/mtr/ss/ip + a scrollable output viewport + --toolbox mode (Phase 3).

Still to come: nmap, multiple concurrent jobs, a Warn state, and an mtr-parsed route-quality row. See PLAN.md.

Built with

Bubble Tea, Bubbles, and Lip Gloss.

Tests

go test ./...          # unit + DAG scheduler + parser + diagnosis
go test -race ./...    # concurrency
go test -fuzz=FuzzSanitize -fuzztime=10s   # terminal-escape sanitizer

Development

The code is split by responsibility:

  • main.go owns CLI arguments, process I/O, and application startup.
  • internal/diagnostic owns target parsing, native probes, per-OS route/SSID lookups, and verdict logic without depending on terminal presentation.
  • internal/ui owns Bubble Tea state, rendering, and tool jobs.
  • internal/textsafe sanitizes untrusted remote and subprocess text shared by both layers.

The UI depends on diagnostics; diagnostics do not depend on the UI. Add network semantics under internal/diagnostic and interaction or rendering behavior under internal/ui.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
diagnostic
Package diagnostic implements target parsing, native network probes, and diagnosis without depending on terminal presentation.
Package diagnostic implements target parsing, native network probes, and diagnosis without depending on terminal presentation.
textsafe
Package textsafe removes terminal control sequences from untrusted text.
Package textsafe removes terminal control sequences from untrusted text.
ui
Package ui owns the Bubble Tea application, rendering, and tool execution.
Package ui owns the Bubble Tea application, rendering, and tool execution.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL