Skip to content

Changelog

[Unreleased]

Added

  • Screenshots in the README: real runs of monitor, dnscheck, http, tls and propagation, rendered as SVG by scripts/screenshot.py and refreshed by the manual Screenshots workflow.

Fixed

  • xping dnscheck scored a domain it could not query at all (every DNS query timed out) 100/100 EXCELLENT. It now reports an error, exits 1, and --min-score / check files fail.
  • Section headers were one column wider than the terminal, so their rule wrapped a ─ onto the next line in terminals that wrap at the edge.
  • The Launchpad PPA release job installs the package's build dependencies (build-essential, pybuild-plugin-pyproject, python3-pytest), which the 1.6.0 upload lacked.
  • scripts/release.py checks PyPI with certifi's certificates when available, so it also runs with the python.org Python on macOS (which has no CA bundle until "Install Certificates" is run); a network error there is reported in one line instead of a traceback.

[1.6.0] - 2026-10-13

Added

  • Fedora and EPEL 10: sudo dnf copr enable mehaskari/xping && sudo dnf install xping, built automatically for every release.
  • xping report shows the 95th percentile, and --since 7d --compare compares each target with the week before: uptime, median and p95 with the change marked green (better) or red (worse).
  • xping monitor --install-service: keep monitoring in the background. The same monitor command runs as a per-user service, a LaunchAgent on macOS or a systemd user unit on Linux, started at login and restarted if it stops, with its output in ~/.xping/monitor.log. The checks are validated before anything is installed. --service-status shows whether it runs; --uninstall-service removes it. No root needed.
  • xping history --clear --older-than 30d deletes only runs older than that and keeps the rest (files left empty are removed), so a monitor saving all day does not grow without bound.
  • VPN awareness. xping net shows a VPN tunnel and whether internet traffic goes through it (found by asking the routing table how 1.1.1.1 is reached, since many VPNs leave the default route alone). xping doctor names the VPN next to the network interface and puts high latency down to the tunnel rather than to your network. --json has internet_interface, vpn_interfaces and via_vpn.

Changed

  • Releases reach the Snap Store's stable channel automatically (each architecture as soon as its build of the release is ready).
  • Snap builds of unreleased commits are versioned X.Y.Z+gitN.HASH; only the release commit itself is X.Y.Z.
  • Packaging held to the distributions' own tools: the Debian package builds in Debian unstable with the tests running and no network, is lintian-clean and passes autopkgtest; a Fedora spec (also used by COPR) builds with %check, passes rpmlint and installs. Both run in CI.
  • xping monitor change lines (in a log or pipe) line up: the check name and type columns are padded to the widest name, so the details start in the same column on every line.

[1.5.2] - 2026-10-07

Fixed

  • xping report with nothing saved says so plainly ("nothing saved yet") and suggests xping monitor --save; a filter or --since that leaves nothing says "in this period" and points to xping history.
  • xping report charts break the line where nothing was measured (e.g. while monitor was stopped) instead of drawing a straight line across.
  • xping monitor and watch mode write each line at once when the output is a file or a pipe, so tail -f on a log follows them live (the output used to sit in a buffer until several kilobytes had accumulated).

[1.5.1] - 2026-10-06

Added

  • A default check file, ~/.xping/checks.toml: xping check and xping monitor use it when no file is given, and say how to create it when it does not exist. xping check --init creates it with checks that work anywhere (your router, 1.1.1.1, 8.8.8.8, DNS, HTTPS); it never overwrites an existing file.
  • xping monitor HOST HOST:PORT URL… without a file: hosts are pinged, HOST:PORT gets a TCP connect, URLs an HTTP check.
  • Homebrew: brew install mehaskari/tap/xping (man page and completion for bash, zsh and fish included). The tap follows PyPI releases on its own, after building and testing each new version on macOS.
  • blocklist checks IPv6 addresses too (nibble format) on the lists that publish IPv6 data, Spamhaus ZEN and DroneBL — both for an IPv6 address given directly and for a domain's IPv6 mail and web servers, which were previously listed as "not checked".
  • xping report: the saved history as one HTML page. An overview of every target (state, uptime, outages, latest value), and per target the uptime, outages and downtime, latest and median value, a chart of the headline metric over time with failures and outages marked, and the list of outages. One self-contained file, no scripts, nothing loaded from the network, light and dark theme. -o FILE (or - for stdout), --since, --last, --title; narrow to one command or target.
  • mtr --tcp / --port PORT: every probe is a TCP SYN to that port (443 by default), like trace --tcp, so mtr works where ICMP is dropped. Also in check files (tcp = true, port).
  • --fail-after N and --recover-after N for watch mode (--watch / --until-up on ping, tcp, udp, http, health) and xping monitor (also per check: fail_after, recover_after). A target counts as DOWN only after N failed checks in a row and as UP again after N passing ones, so a single blip neither flips the state nor alerts anybody. Uptime still counts every check; outages, the "down at the end" verdict and alerts follow the confirmed state, and an outage is measured from its first failed check. --until-up waits for N passes. ping --watch keeps its 3-lost-pings rule as the default.
  • xping monitor marks a check ◐ while its latest runs disagree with the confirmed state.

[1.5.0] - 2026-10-05

Deprecated

  • Short options that meant something else in one command than in all the others. The old spelling still works until 2.0 and prints a warning on stderr:
  • trace -p N (probes; -p is --ports elsewhere): use --probes N.
  • propagation -t TYPE (-t is --timeout elsewhere): use -r / --type.
  • ping -w (-w is --workers elsewhere): use --watch.
  • wifi -i NAME (-i is --interval elsewhere): use -I / --interface.

Added

  • xping monitor FILE: a live dashboard for a check file. Every check runs again and again, each on its own schedule (every in the file, or --every). In a terminal the state is a table redrawn in place: up or down, the latest value of the check's headline metric, a trend of the last 16 runs, uptime and how long the current state has lasted; narrow terminals drop the least important columns, then shorten check names, and hide the detail rather than show two letters of it. Elsewhere (cron, systemd, a pipe) it prints one line per state change. --notify / --webhook alert on every change, --save keeps each result in the history under the same name as the single command, --rounds N stops after N runs. Ctrl-C ends with a summary (uptime, outages, longest outage, average); the exit code says whether everything was up. Durations drop a zero remainder: 1m rather than 1m00s.
  • ping --until-up and --every, like tcp, udp, http and health: ping once per check until the host answers, then exit 0 (xping ping 10.0.0.1 --until-up -q && ssh 10.0.0.1).
  • Check files support trace, mtr, wifi, doctor and speedtest, with their thresholds (max_loss / max_latency for mtr, min_signal for wifi, min_download / min_upload for speedtest). Speedtest checks run on their own after the parallel ones.
  • xping check --save keeps the whole report in the history, one history per check file.
  • speedtest --min-download MBPS and --min-upload MBPS: exit 1 when the link is slower than that.

Changed

  • Every terminal view is now tested with realistic results, with colour on and off: the layout must be identical either way (the cause of past column drift), and escape codes in remote text (whois, DNS, certificates) must never reach the terminal. Coverage rose from 79% to 82%, and the minimum from 75% to 80%.
  • mypy now checks every module: the last three on the baseline (exporters/tables.py, verdict.py, diagnostics/speedtest.py) are typed, and the baseline is gone.
  • ping --watch now judges the whole session when you stop it: --max-loss / --max-latency and the exit code apply (1 when every ping was lost), and --save keeps the session in history. An unresolvable host now exits 1 instead of 0.
  • Python 3.13 and 3.14 are tested in CI (Linux and macOS) and listed as supported on PyPI. Python 3.10 stays supported: Ubuntu 22.04, which the PPA builds for, ships it.
  • --save confirms the save with the run number and the xping history command to use, except with -q or an export format, where the output has to stay clean.

Fixed

  • xping.diagnostics.check can be imported on its own (it failed with a circular-import error when it was the first xping module loaded, e.g. from a script using xping as a library). The pass/fail verdicts moved from xping.cli.verdict to xping.verdict, below the CLI layer; a test now imports every module standalone and keeps the lower layers free of CLI imports.
  • xping history no longer shows an empty trend line for a single run, and says "1 run" rather than "1 runs".

[1.4.9] - 2026-10-03

Added

  • History: --save and xping history.
  • --save on ping, trace, mtr, health, tls, tcp, udp, http, smtp, dnscheck, blocklist, ntp, speedtest, wifi and doctor keeps the result in ~/.xping/history/<command>/ (or set save = true under [defaults] in the config file).
  • xping history lists what is recorded.
  • xping history COMMAND TARGET shows the runs with the command's key metrics, a trend line, the pass rate, and the latest run against the median. It supports --last, --since 7d, --clear, and --json / --csv.
  • Nothing is recorded without --save. The newest 500 runs per command and target are kept.

Changed

  • xping diff is easier to trust and to read.
  • Jitter and the maximum RTT get a 10% noise band; other metrics keep 3%.
  • New notes warn when the two runs have different or small sample sizes.
  • Notes also explain likely causes: a higher minimum RTT points to a route or ISP change, and higher jitter with an unchanged minimum points to a busy or weak local link.
  • JSON output has a new notes field.
  • xping wifi names two stronger access points when they are nearly equal, so the tip no longer flips between runs as signals fluctuate.

Development

  • CI type-checks the code with mypy. Every module is checked except three that need a larger rework, which are listed as a baseline in pyproject.toml. Fixing the reported issues turned up a name that shadowed the colour helper in lookup, plus many missing None checks and annotations.
  • The minimum test coverage rose from 55% to 75% (actual: 79%). The CI run page shows a coverage summary.

  • A documentation site at https://mehaskari.github.io/xping/, built with MkDocs (Material) from docs/. It has search, dark mode and a table of contents. Every docs change is built in strict mode, so a broken link or section anchor fails the check. The site is published from main.

[1.4.8] - 2026-09-27

Changed

  • xping wifi detects a clearly stronger access point nearby (8 dB or more, same security, same name where known). This is a device "stuck" on a far access point, and toggling Wi-Fi fixes it. 5/6 GHz access points are preferred.
  • xping smtp lists MX records one per line, showing the first three. smtp and tls show certificate names short ("Google Trust Services (WE2)" instead of the full distinguished name); JSON output keeps the full names.
  • CI and release workflows use current action versions, which run on Node.js 24 instead of the deprecated Node.js 20: checkout v7, setup-python v7, upload-artifact v7, download-artifact v8 (a hash mismatch now fails the download), action-gh-release v3 and CodeQL v4. All of them are still pinned to commit SHAs. Dependabot now proposes action updates as one grouped PR.

[1.4.7] - 2026-09-27

Security

  • Terminal escape sequences and control characters from servers are removed before display and CSV/Markdown export. Before, a malicious server could move the cursor, overwrite output or write to the clipboard.
  • The DNS parsers no longer hang on a compression pointer loop, which a malicious response could trigger.
  • Raw DNS queries use a random transaction ID on a connected socket and drop mismatched replies, which makes spoofing hard.
  • dig gets names through -q / -t, so a name such as -f/etc/passwd from a check file can no longer read local files.
  • profile list --names prints only valid names. Bash completion passes them to compgen -W, which expands $(...).
  • http counts the body instead of storing it, and stops at 100 MB. WHOIS responses are capped at 1 MB.
  • The completion rc block single-quotes the script path. CI and release workflows use read-only tokens and pass secrets as environment variables.
  • Every GitHub Action in CI and release workflows is pinned to a commit SHA, so a moved or hijacked tag can't change what runs with release credentials. Dependabot keeps the pins current.

Added

  • xping smtp HOST — checks a mail server.
  • It checks the greeting, the EHLO extensions, STARTTLS (or implicit TLS on 465) and certificate verification for the server's name.
  • It shows the AUTH mechanisms and size limit offered over TLS, and whether the server's reverse DNS resolves back.
  • A mail domain is tested through its preferred MX; --no-mx tests the host itself.
  • It ends with QUIT and never sends mail.
  • --port 25|587|465. Missing or invalid TLS is a warning unless --require-tls is given. --min-days checks certificate expiry.
  • It is an smtp type in check files.
  • xping diff BEFORE AFTER — compare two --json results of the same check. Either side can be - for stdin.
  • It recognises the kind of result and compares the numbers that matter, each with its "better" direction: RTT, jitter, loss, HTTP phases, TLS days, speed, clock offset, Wi-Fi signal and so on.
  • It shows the route changes of trace / mtr, added and removed DNS records, and status changes in dnscheck / doctor / check / blocklist.
  • Changes under 3% count as noise.
  • --max-regression PCT exits 1 on a bigger regression or a worse status.
  • xping wifi — Wi-Fi link quality.
  • It shows signal and noise (dBm) with a grade, the SNR, the channel, band and width, the link rate and MCS, the standard, the security, and how many nearby networks share (or on 2.4 GHz overlap) your channel.
  • It gives advice: move closer, interference, switch to 5 GHz, or the least used of channels 1 / 6 / 11.
  • --nearby lists the networks around you. --min-signal DBM makes it an alarm.
  • Data comes from system_profiler (macOS), iw / nmcli (Linux) or netsh wlan (Windows), with no root and no network traffic.

Changed

  • xping blocklist shows a compact summary instead of one row per list per address, which was 43 rows for a typical domain.
  • Each checked address or domain gets one row, e.g. "clean on 8/8 IP lists".
  • Any result that is not clean is still shown in full.
  • --all restores the full table.
  • The closing line now says "43 checks on 11 lists" instead of the misleading "43 lists".
  • xping ntp shows the measurement accuracy (± half the round trip), and says when the offset is within it. JSON output has a new uncertainty_ms field.
  • xping udp and xping ntp no longer repeat the address on an "IP" line when the target already is an IP.

[1.4.6] - 2026-09-26

Added

  • xping udp HOST PORT — is a UDP service answering? Each probe is open (a reply came back), closed (ICMP port unreachable) or no response (filtered, or the service ignored the request). The request is chosen by port — a DNS root query for 53/5353, SNTP for 123, SNMPv2c GET sysDescr (community public) for 161 — or set with --probe / --payload HEX. It supports --max-latency, --watch, --until-up and alerts, and is a udp type in check files.
  • xping blocklist IP|DOMAIN — checks spam blocklists (DNSBL).
  • An IP is checked on Spamhaus ZEN, SpamCop, Barracuda, PSBL, Mailspike, UCEPROTECT L1, DroneBL and s5h.
  • A domain is checked on Spamhaus DBL, SURBL and URIBL, and the IPv4 addresses of its MX and A hosts are checked on the IP lists.
  • Results are listed, policy (Spamhaus PBL end-user ranges, which don't fail), clean, refused (lists that refuse public resolvers) or error.
  • --zone adds more lists. It is a blocklist type in check files and exits 1 when the target is listed.
  • DNSSEC in xping dnscheck.
  • Three queries to a validating resolver (1.1.1.1, then 8.8.8.8) with the DO flag: DS at the parent, a signed SOA and its AD flag, and a checking-disabled retry after SERVFAIL.
  • Results: signed and validated (ok); broken "bogus" signatures, meaning validating resolvers cannot resolve the domain (fail); signed without DS, i.e. not active (warn); not signed (info).
  • Answers are retried over TCP when truncated.
  • xping lookup --doh PROVIDER — DNS over HTTPS (RFC 8484 wire format) to cloudflare, google, quad9 or any https:// URL. It gets past resolvers that filter, rewrite or hijack plain DNS. JSON output now includes transport (dig, udp or doh) and resolver.
  • Personal defaults in ~/.xping/config.toml.
  • A [defaults] section applies to every command, and there is one section per command (e.g. [ping] count = 10, [lookup] doh = "cloudflare"). Keys are long option names.
  • The command line always wins. In mutually exclusive pairs, the flag given on the command line drops the file's value for the other one (-6 beats ipv4 = true).
  • Unknown sections, options or values are errors that name the file, section and key.
  • xping config shows the active settings or explains what is wrong, and xping config --example prints a starter file.
  • XPING_CONFIG selects another file, and XPING_CONFIG=none ignores it.
  • The xping HOST shorthand uses the [ping] section.
  • xping ntp [SERVER] — the system clock offset against an NTP server (default pool.ntp.org) over SNTP. It shows the offset, the delay, the stratum, the reference and whether the server is synchronised, using the lowest-delay of several samples. --max-offset MS makes it an alarm. It is an ntp type (threshold max_offset) in check files.

[1.4.5] - 2026-09-26

Added

  • xping doctor [host] — answers "why is my internet not working?". It checks, in order: network interface (and DHCP), default gateway, internet by IP (no DNS), DNS (your resolvers vs. a public one), connection quality, captive portal, HTTPS (interception and system clock), IPv6 and, optionally, a host (--port, default 443). It ends with one plain-language diagnosis and what to do. Exit code 1 when a step fails; --json, --csv (one row per step) and --markdown work as usual.
  • xping http: per-phase timing waterfall and security headers.
  • The TLS handshake is now timed separately from the TCP connect. The time to download the body and the time spent following redirects are timed too.
  • The phases are drawn as a waterfall, like a browser's network tab.
  • The TLS version and cipher are shown.
  • A security-header audit covers HTTPS / http→https upgrade, HSTS (max-age ≥ 180 days), CSP, X-Content-Type-Options, X-Frame-Options (or CSP frame-ancestors), Referrer-Policy, Permissions-Policy, and version-revealing Server / X-Powered-By headers. It is informational only.
  • New JSON fields: tls_ms, transfer_ms, redirect_ms, tls_version, tls_cipher, security, security_missing. CSV / Markdown gain a "Security headers" table.

  • xping trace --tcp / --port N — traceroute with TCP SYN probes to a real service port (443 by default; --port implies --tcp). It gets through firewalls that drop ICMP, where a normal traceroute ends in * * *. No root needed: Linux reads the router from the TCP socket's error queue (IP_RECVERR), macOS from an unprivileged ICMP socket. IPv4 and IPv6; not available on Windows.

  • Alerts in watch mode: --notify and --webhook URL on ping --watch and on --watch / --until-up for tcp, http and health.

  • An event fires on every DOWN / UP change, and the UP message includes how long the outage lasted.
  • --notify uses Notification Center (macOS) or notify-send (Linux), and the terminal bell elsewhere.
  • --webhook POSTs a JSON event with text / content fields that Slack, Discord, Mattermost and Google Chat display directly. It is sent in the background, and a failure is reported only once.
  • ping --watch treats three consecutive lost pings as an outage.

Changed

  • xping http: tcp_ms is now the TCP connect time alone. It used to include the TLS handshake, which is now reported as tls_ms.

Fixed

  • The progress spinner (used by doctor, http, trace, net, …) printed its message on one line per animation frame when the message was wider than the terminal. It is now cut to fit, with "…".
  • Misaligned columns in http (timing, connection, security and response headers), trace (host column after the RTT) and all (summary). Padding counted the invisible colour codes, so columns drifted, especially with colour off (NO_COLOR, piped output). Coloured text is now padded by its visible width.
  • Summary tables (trace, ping, watch, speedtest, …) had a misaligned value column and uneven borders whenever a cell was coloured; the table renderer measured cells including their colour codes.
  • xping trace: the rule under the hop list was shorter than the rules around the column header; all rules now have the same width.
  • xping trace printed the separator line twice before its summary.
  • xping http: the last line of the redirect chain always showed status 200, even when the final response was an error.
  • Project links pointed to github.com/mehdiaskari/xping, which does not exist. The correct repository, github.com/mehaskari/xping, is now used in the PyPI metadata (pyproject.toml, setup.cfg), xping about, the man page, the snap and Debian metadata, README and CONTRIBUTING.
  • The WHOIS/RDAP bootstrap download (data.iana.org) now uses the same TLS context as every other HTTPS request (certifi, TLS 1.2+), so it also works on Python builds without a usable system CA store.

Docs

  • New user guide, docs/guide.md. It covers every command with all options and defaults, what the output means, and how scores are computed (health, dnscheck, speedtest, osdetect). It also has the check-file reference, recipes, files/privacy, platform notes and a FAQ. It is linked from the README and the PyPI project links. A test fails if a command or long option is missing from it.
  • Man page: --proto udp in the listen description rendered as --protoudp.
  • README: current install options (PPA for Ubuntu 24.04 and derivatives, Snap with stable/edge channels, PyPI with the completion step). The non-existent AUR package is removed. New sections for listening ports, OS fingerprint and "Files & privacy" (what is stored, which commands contact third parties), plus completion environment variables.
  • CONTRIBUTING: PR workflow with branch protection, an up-to-date "add a new command" checklist (verdicts, CSV tables; completion is automatic), and notes on the real-shell completion tests.
  • RELEASE: the PR-based release flow, macOS-compatible sed, the current CI matrix, snap edge → stable promotion, and a correct rollback (PyPI yanking happens on the website; twine yank does not exist).
  • SECURITY: 1.4.x supported; the design section describes unprivileged ICMP, strict TLS, which files are written, third-party contacts, and shell-free subprocesses. The obsolete --output mention is removed.
  • PACKAGING: completion packaging, snap channels, six-file version checklist, and the corrected PPA series (noble).
  • Man page: PRIVACY section, accurate DEPENDENCIES, completions in FILES.
  • Debian: a real README.Debian instead of the template placeholder, and the unused xping-docs.docs is dropped. The bug report template asks for the install method and shell.

[1.4.1] - 2026-09-25

Fixed

  • Tab completion did not work (reported on macOS). There were three causes:
  • the zsh script never registered itself when sourced;
  • zsh's own _hosts completion also claims a command named xping and took over;
  • on macOS's bash 3.2, the documented source <(xping completion bash) silently does nothing.

Completion scripts for bash, zsh and fish are rewritten and verified by pressing Tab in real shells (zsh via zpty, bash 3.2 and 5, fish 3.7).

Added

  • xping completion --install / --uninstall: sets up completion for your shell in one step. It writes the script and adds a marked, idempotent block to ~/.zshrc, ~/.bashrc or ~/.bash_profile (bash on macOS); fish is set up via ~/.config/fish/completions/.
  • Richer completion: option descriptions (zsh, fish), fixed-choice values (--proto, --type, shells), hostnames plus saved profile names wherever a host is expected, .toml/.json files for xping check, and profile names for profile show/remove. In zsh, mutually exclusive options are not offered together.
  • The Debian/Ubuntu package installs completion for bash, zsh and fish automatically; the snap provides bash completion.
  • xping profile list --names prints bare profile names (used by completion).

[1.4.0] - 2026-09-25

Added

  • Exit codes you can script with: every command exits 0 when its check passes and 1 when it fails (unreachable, closed port, HTTP ≥ 400, expired certificate, DNS error, …); invalid usage exits 2, Ctrl-C 130. Previously almost everything exited 0.
  • Thresholds: ping --max-loss/--max-latency, tcp --max-latency, mtr --max-loss/--max-latency, http --max-latency/--expect-status, tls --min-days, health/dnscheck --min-score. The reason a threshold failed is printed on stderr.
  • -q / --quiet on every diagnostic command: no output, exit code only.
  • IPv6: IPv6-only hosts now work, and -4 / -6 force a family on ping, trace, mtr, tcp, portscan, tls, http, health, mtu, osdetect and all. System fallbacks use ping -6/ping6, traceroute -6/ traceroute6/tracert -6.
  • No root needed for ping, trace, mtr and health on macOS and Linux: xping uses unprivileged ICMP ("ping") sockets, with raw sockets and the system tools as fallbacks. Traceroute reads "time exceeded" replies from the socket (macOS) or the IP_RECVERR error queue (Linux). xping deps shows which mode is available.
  • trace --asn / mtr --asn: the network operator (AS number and name) of every hop, via Team Cymru's DNS interface. Opt-in.
  • xping net: interfaces (state, MTU, addresses), default gateways, DNS resolvers, local source addresses and public IPv4/IPv6 with country and Cloudflare colo. --no-public keeps everything local.
  • xping propagation: compare a record (A, AAAA, CNAME, MX, NS, TXT) across your resolver and six public ones; --expect makes it a pass/fail propagation check.
  • --watch / --until-up for tcp, http and health: a live up/down log with downtime and an uptime summary, or "wait until it's up, then exit 0" for scripts. Thresholds apply in watch mode too.
  • xping check <file>: run many ping/tcp/http/tls/lookup/dnscheck/ health/propagation checks from one TOML or JSON file in parallel, with one exit code. xping check --example prints a starter file.
  • Multi-connection speedtest (-c/--connections, default 4; -d/--duration): a single stream under-reports fast links. Upload is timed after the TLS handshakes; the server row shows the Cloudflare colo.

Changed

  • --csv and --markdown are real tables now: one row per ping reply, hop (with per-probe columns), port, host, DNS record, check, … instead of Python repr() of nested lists. Field-type results (tls, whois, http, health, speedtest, …) export field,value rows. JSON is unchanged.
  • --json, --csv, --markdown and --quiet are mutually exclusive.
  • Shell completion is generated from the argument parser, so it always matches the real commands and flags.
  • xping http records the address it connected to (ip); mtu exports its header overhead (28 for IPv4, 48 for IPv6).

Fixed

  • speedtest wrote the download error message into the server field.
  • Parallel health checks could corrupt ~/.xping/health_history.json (history writes are now serialized).
  • Man page: the health options were listed under a duplicated mtu heading, trace promised a path tree it does not draw, and two invalid roff escapes broke rendering in mandoc.

[1.3.8] - 2026-09-25

Security

  • Every TLS/HTTPS connection (tls, http, whois RDAP, speedtest) now goes through one shared context that verifies certificates and refuses anything older than TLS 1.2.
  • whois no longer retries RDAP with certificate verification turned off when the verified request fails.

Fixed

  • --json output dropped several values shown on screen: http now exports dns_ms, tcp_ms, http_version and h2_supported, health exports history, and tls exports chain.
  • trace read the ICMP type at a fixed offset, misparsing replies whose IPv4 header carries options; the header length is now honoured.
  • The system traceroute/tracert fallback ignored --timeout.
  • tcp resolved the host, then connected by name (resolving again, and possibly to a different address than the one displayed).
  • ping --watch silently ignored --json/--csv/--markdown; the combination is now rejected with a clear error.
  • osdetect could crash on a TTL above 255; dead TTL tables removed.
  • Removed an unreachable except branch in whois and unused code in trace and speedtest.
  • http failed on every HTTP/2-capable HTTPS site: the request connection offered h2 via ALPN, but http.client only speaks HTTP/1.1, so servers that picked h2 rejected the request. The request now negotiates HTTP/1.1 only; HTTP/2 support is detected with a separate ALPN probe and shown as "HTTP/2 support".
  • dnscheck always failed DMARC: it looked for the record at the apex instead of _dmarc.<domain>. The policy is now read from the p= tag (so sp=reject no longer masks p=none). DKIM is detected by probing common selectors at <selector>._domainkey.<domain>.
  • listen found nothing on macOS, and the netstat fallback mis-parsed Linux (0.0.0.0:22, state vs PID column) and dropped Windows UDP rows. macOS/BSD now use netstat -an with tcp4/tcp46 and dot-separated ports supported.
  • Raw-UDP DNS fallback returned CNAME targets as IP addresses: answers are now filtered by the queried record type.
  • DNS query failures were reported as missing records: SERVFAIL, REFUSED and timeouts are now tracked separately (query_errors in lookup output). dnscheck marks those checks "not verified" and leaves them out of the score instead of failing them.
  • speedtest and osdetect now accept --json / --csv / --markdown (osdetect returns a proper result model).

Internal

  • The command list is now defined once (cli/main.py), and tests check that the parser, shell completion, and --help stay in sync with it.
  • CI's version check now also covers setup.cfg, snap/snapcraft.yaml and debian/changelog; a non-blocking Windows test job was added.
  • Resolved the open CodeQL findings (empty except, import * shims, unused imports/variables, asserts with side effects).

Docs

  • Removed the nonexistent --alarm and --output flags from the man page, shell completion and snap description. README now names the certifi dependency and the correct PPA package (xping). --help lists every command.

[1.3.7] - 2026-09-21

Changed

  • The ASCII-art banner now prints only when xping is run with no arguments at all. It no longer appears before the bare-host shorthand (xping 8.8.8.8) or before any explicit subcommand (xping ping ..., xping trace ..., etc.) — every real command now starts straight with its own output.

[1.3.6] - 2026-09-20

Fixed

  • PPA rejected on upload: Launchpad refused the 1.3.5 source package outright with Rejected: Unable to find distroseries: unstable. debian/changelog's distribution field said unstable (Debian terminology), but Launchpad PPAs only recognise real Ubuntu series codenames — noble, jammy, etc. Fixed by changing it to noble (Ubuntu 24.04 LTS), matching the series this PPA has targeted since 1.3.0.

[1.3.5] - 2026-09-18

Fixes a second, distinct Launchpad PPA signing failure that only became visible after 1.3.4 fixed the first one and the workflow reached the actual signing step for the first time.

Fixed

  • PPA GPG signing: debsign was failing with gpg: Sorry, we are in batchmode - can't get input for any signing key that has a passphrase — strict batch mode refuses to prompt for one under any circumstances. Fixed by writing the passphrase to a private file on the runner (via a new LAUNCHPAD_GPG_PASSPHRASE secret — safe to leave empty if the key has none) and signing through a gpg --passphrase-file wrapper passed to debuild via its -p<command> option. This single mechanism now works whether or not the signing key has a passphrase, so there's no longer a "pick one setup or the other" decision to make.
  • Verified end-to-end locally before shipping, not just reasoned about: generated a real, throwaway passphrase-protected GPG key and ran debsign -p<wrapper> --clearsign against it — the exact mechanism debuild -S -sa invokes internally — and confirmed it signs with zero prompts and exit code 0.

Changed

  • PACKAGING.md: documented the new LAUNCHPAD_GPG_PASSPHRASE secret requirement; removed the earlier passphrase-less-key requirement, which no longer applies.

[1.3.4] - 2026-09-13

This release fixes the actual root causes behind PPA and Snap builds repeatedly failing, verified by running the real build/test/lint tools locally rather than editing text and assuming it would work.

Fixed — data correctness

  • Systemic export bug: exporters/serialize.py used dataclasses.asdict(), which flattens nested dataclasses before computed @property fields can be attached. This silently dropped fields like MtrHop.loss_pct inside MtrResult.hops, and would have done the same for PingResult.avg_rtt inside HealthResult.ping, in every JSON/CSV/Markdown export. Rewritten to walk the real object graph so nested dataclasses at any depth keep their computed fields.

Fixed — silently broken features

  • render/views/http.py and render/views/portscan.py each contained two definitions of the same function (a leftover from an earlier edit that appended new code without removing the old). Python keeps the last definition, so the older, less capable version was silently running in production — the HTTP timing breakdown and the portscan --banners output were both quietly disabled despite the correct code sitting right above, unreachable.
  • render/latency.py had orphaned unreachable code (referencing undefined names) left over from a previous edit — harmless at runtime since it was unreachable, but removed for good.
  • diagnostics/whois.py was missing a top-level import ssl — masked by a local import inside one function, but referenced elsewhere as an unresolvable string type-annotation.

Fixed — CI pipeline

  • ruff.toml had line-length/target-version nested under [lint] instead of top-level, which is invalid TOML for ruff's schema — this made the "Code Quality" job fail immediately on a config parse error, before linting a single file, on every run.
  • Actually ran ruff check and ruff format against the whole codebase for the first time and fixed every real finding (a missing import, an ambiguous variable name, an unused loop variable, a zip() missing strict=), then reformatted all 94 files for a consistent baseline. Two purely stylistic rules (E701/E702) are intentionally ignored — documented inline in ruff.toml — because this codebase deliberately uses compact one-line guard clauses throughout, and mechanically rewriting ~50 of them risks introducing typos for zero functional benefit.
  • Coverage fail_under lowered from an unreachable 85% to 55%, based on an actually-measured 63% on the current suite.
  • Fixed 5 real test failures, each verified individually and then as a full suite (222/222 passing):
  • TestMtr::test_mtr_result_to_dict, test_export_json_mtr — both resolved by the serialize.py fix above.
  • test_lookup_with_dig_path — mock signature didn't match the real 3-argument _dig_query(host, rtype, server).
  • test_lookup_socket_fallback — mocked a function lookup() never actually calls, so the test silently fell through and made a real network query in CI.
  • test_subprocess_trace_live_parses_hops — didn't mock trace_tool()/trace_command(), so the result depended on whether the traceroute binary happened to be installed on the runner.

Fixed — Ubuntu PPA (Launchpad)

  • debian/control still listed pybuild-plugin-pyproject in Build-Depends, which does not exist on Ubuntu 22.04 (Jammy) — the exact series this PPA builds for. This alone failed dpkg-checkbuilddeps before the build could even start, on every single upload.
  • Added a setup.py shim and completed setup.cfg metadata so pybuild can build via the classic distutils interface without the pyproject-only plugin.
  • debian/rules switched to PYBUILD_SYSTEM=distutils, skips tests during package build, and installs the man page.
  • GPG signing was failing with Inappropriate ioctl for device — debsign tried to open a controlling terminal that does not exist on a GitHub-hosted runner. Fixed by configuring gpg.conf/ gpg-agent.conf for batch mode with loopback pinentry and no-tty, the standard approach for headless package signing. See PACKAGING.md for the requirement that the signing key have no passphrase.
  • Filled in debian/copyright (was still the unfilled template).

Fixed — Snap Store

  • Removed .github/workflows/release-snap.yml entirely. It duplicated the Snap Store's native GitHub integration (already authorized via Snapcraft.io → Builds → "build-snapcraft-io"), used a SNAPCRAFT_TOKEN secret that was never configured, and failed on every single run with login_data is empty — while the real, working publish path (the native integration) had been succeeding the whole time in parallel. Keeping a workflow that can never pass is worse than not having it: it manufactures a permanent false failure signal for something that already works.

Changed

  • Author contact corrected to Mehdi Askari iorganamis@gmail.com everywhere (was still mehdiaskari@outlook.com / "Meh Askari" in several files that earlier passes had missed).

[1.3.0] - 2026-07-08

Added

  • xping HOST bare shorthand — auto-pings and shows follow-up suggestions
  • xping completion bash|zsh|fish — shell tab-completion
  • xping speedtest — download/upload speed via Cloudflare
  • xping listen [--proto tcp|udp] — local listening ports
  • xping osdetect HOST — OS fingerprinting from ICMP TTL
  • xping dnscheck DOMAIN — DNS health check: SPF, DMARC, DKIM, MX, NS
  • xping portscan --banners — service banner grabbing
  • xping lookup --server IP — custom DNS resolver
  • HTTP/2 detection via ALPN in xping http
  • TLS certificate chain display in xping tls

[1.2.5] - 2026-06-28

Fixed

  • whois.py duplicate function definitions
  • RDAP SSL error on macOS / HTTPS fallback for blocked port 43

[1.2.0] - 2026-06-25

Added

  • rdns, tls, http, whois, health, profile, mtr, mtu commands
  • ping --watch continuous live mode
  • Network health score with history
  • Saved target profiles