Website · Install · Quick Start · Modes · CLI Reference · JSON Output · Security
pinghue is a colored, concurrent ICMP/TCP ping monitor for maintenance windows. It gives operators a dense terminal view for many hosts at once and can also write structured JSON for reports, cron jobs, and CI checks.
Current version: 5.0.0. The command-line interface and JSON output are stable public interfaces. JSON output uses schema_version: 1; breaking JSON changes require a new schema version.
Why pinghue
| Dense at-a-glance view Watch many hosts in one readable terminal table — per-host latency, loss, jitter, and a live history bar. |
ICMP & TCP Default ICMP, or TCP connect checks with -p. Up to 1024 concurrent probes. |
Evidence-ready JSON--output writes a stable schema_version 1 summary for reports, cron, and CI. |
| Whole-run accuracy Host states reflect everything that happened in the window, not just the most recent probes. |
Scriptable--no-tui, --count, --duration, and fail-on-down exit codes for automation. |
Local & safe No server, no daemon, no credentials. Runs on macOS and Linux, Python 3.10–3.13. |
Install
Recommended isolated installs from PyPI:
uv tool install pinghue
or:
pipx install pinghue
Plain pip also works inside a virtual environment:
python -m pip install pinghue
Homebrew is available through the inxbit/tap tap:
brew install inxbit/tap/pinghue
The tap repository is inxbit/homebrew-tap; Homebrew exposes it as inxbit/tap.
For contributors, install from a local clone in editable mode:
git clone https://github.com/inxbit/pinghue.git
cd pinghue
python -m venv .venv
. .venv/bin/activate
python -m pip install -e ".[dev]"
The dev extra installs the local package plus the test, type-checking, linting, build, and schema-validation tools used by the repository.
Quick Start
pinghue 1.1.1.1 8.8.8.8 example.com
pinghue -f hosts.txt
pinghue -p 443 example.com
pinghue -p 1 127.0.0.1 -c 1 --no-tui
pinghue --output maintenance.json 1.1.1.1 example.com
pinghue --output maintenance.json --overwrite 1.1.1.1 example.com
pinghue --host-label maintenance-window --output maintenance.json 1.1.1.1
Host files are plain text. Blank lines and # comments are ignored. Inline comments
on host lines are also ignored (host.example # comment).
Host files must be non-symlink regular files, at most 1 MiB, and at most 5,000
lines. A run accepts at most 5,000 unique targets in total, and each target string
is capped at 253 characters.
# edge and core checks
1.1.1.1
8.8.8.8
example.com
internal-db.corp
What This Is
- A focused terminal monitor for maintenance windows, migrations, and quick reachability checks.
- A concurrent ICMP/TCP probe runner with a readable Textual TUI.
- A scriptable probe tool with
--no-tui,--count,--duration, and--output. - A JSON-producing report helper for post-maintenance evidence.
- A local operator tool designed for macOS and Linux.
What This Is Not
- Not a Prometheus, Smokeping, Zabbix, or NMS replacement.
- Not a long-running metrics database or alerting system.
- Not a privileged daemon.
- Not a packet capture or traceroute tool.
- Not a service that accepts remote network requests.
Supported Platforms
The supported platform contract is macOS and Linux on Python 3.10 through 3.13. CI runs on both macOS and Linux for every supported Python version.
The TUI assumes an ANSI-capable terminal with Unicode glyph support. Windows and other POSIX platforms are outside the declared support scope unless explicitly added later.
Stability Policy
pinghue treats CLI flags and JSON exports as compatibility contracts. schema_version: 1 is the JSON v1 contract: additive fields are non-breaking, but removing fields, changing field types, changing enum values, or changing required-field behavior requires a new schema version.
CLI removals, flag renames, and incompatible behavior changes are deprecated for at least one minor release before removal. Deprecated flags continue to parse during that window and release notes identify the replacement. Patch releases do not intentionally break CLI or JSON consumers.
A narrowly scoped safety or security limit may be enforced without that deprecation window when preserving the old behavior would leave a material resource-exhaustion or integrity risk. The exception must be identified in the release notes and, when it changes accepted CLI behavior, ships only in a major release.
Starting with 1.0.0, release versions follow semantic versioning: patch releases are bug fixes, minor releases may add compatible behavior, and major releases are reserved for breaking CLI or JSON changes.
2.0.0 changes the default --output behavior: existing regular files are preserved unless --overwrite is passed. Scripts that intentionally reuse the same JSON path should add --overwrite.
Modes
pinghue defaults to ICMP mode:
pinghue 1.1.1.1 example.com
TCP mode is enabled by passing a port:
pinghue -p 443 example.com api.internal
Use no-TUI mode for scripts, cron, CI, and package smoke tests:
pinghue -p 443 example.com -c 3 --no-tui
Example no-TUI output:
2026-05-14T18:32:11.420000+00:00 1.1.1.1 ok latency=9.20ms
2026-05-14T18:32:11.421000+00:00 example.com ok latency=14.08ms
2026-05-14T18:32:12.420000+00:00 api.internal timeout latency=-
CLI Reference
pinghue [OPTIONS] [TARGET ...]
| Option | Default | Description |
|---|---|---|
TARGET ... |
none | Hostnames or IP addresses to probe, up to 253 characters each. Required unless --check is used. |
-f, --file PATH |
none | Read targets from a non-symlink plain-text host file. Blank lines and full-line/inline # comments are ignored. |
-p, --port PORT |
ICMP | Enable TCP connect checks against PORT. Valid range: 1-65535. |
--ipv4 |
off | Force IPv4 resolution/probing. |
--ipv6 |
off | Force IPv6 resolution/probing. |
-n, --numeric |
off | Skip DNS and require IP literals. |
-i, --interval SEC |
1.0 |
Seconds between probes. Minimum: 0.1. |
--timeout SEC |
interval | Per-probe timeout in seconds. Must be greater than 0. |
-c, --count N |
continuous | Stop after N probes per target. Intentionally has no upper bound. |
--duration SEC |
continuous | Stop after elapsed seconds. Intentionally has no upper bound. |
--no-tui |
off | Print one line per probe instead of launching the TUI. |
--output PATH |
none | Write a JSON run summary on exit. - writes to stdout, requires --no-tui, and moves per-probe lines to stderr so stdout carries only the JSON document. Existing regular files are not replaced unless --overwrite is set. |
--overwrite |
off | Allow --output to rewrite an existing single-link regular file in place. The rewrite is type-safe but not crash-atomic. |
--output-mode {private,umask} |
private |
Permissions for the --output file: private (0600, owner only) or umask (honor the process umask). |
--no-samples |
off | Emit empty per-target samples arrays in JSON output. |
--concurrency N |
64 |
Maximum concurrent probes, 1-1024; ICMP daemon workers are bounded by this limit. |
--jitter-threshold MS |
50.0 |
Mark jitter as attention-worthy above this RFC 3550 interarrival jitter, in milliseconds. |
--fail-threshold COUNT |
3 |
Classify a previously responsive host as down after this many consecutive failures; all-failure runs are down immediately. |
--fail-on-any-down |
off | Exit 3 when any target finishes down. |
--fail-on-all-down |
off | Exit 3 only when all targets finish down. --fail-on-down remains a compatibility alias. |
--history-style STYLE |
bar |
One of bar, dots, sparkline, or none. |
--check |
off | Run the environment doctor and exit. |
--resolve-name HOST |
example.com |
With --check, resolve this host for DNS diagnostics, up to 253 characters. Defaults to the first target when provided. |
--quiet |
off | With --check, suppress output and use only the exit code. |
--host-label LABEL |
local |
Operator-controlled host label written to JSON output, up to 128 characters. |
-v, --version |
none | Print the installed version. |
-h, --help |
none | Print help. |
Exit codes
| Code | Meaning |
|---|---|
0 |
Run completed (and no --fail-on-* condition triggered). In --no-tui mode, interrupting a run with Ctrl-C still evaluates the fail conditions, matching classic ping behavior; the JSON exit_reason field records interrupted. In the TUI, use q, which records user_quit. |
1 |
Runtime error (for example the --output file could not be written), or --check found the environment not ICMP-ready. |
2 |
Usage error: unknown flag or invalid value. |
3 |
--fail-on-any-down / --fail-on-all-down condition triggered. |
TUI Controls
| Key | Action |
|---|---|
q |
Quit. |
a |
Show or hide resolved addresses. |
r |
Reset the selected host. |
R |
Reset all hosts. |
b |
Probe the selected host immediately. |
B |
Probe all hosts immediately. |
History Legend
The default history style is a fixed-scale colored bar:
- Green bar: successful probe.
- Amber bar: successful probe above the slow-latency threshold.
- Red
./·: timeout, loss, or down state. - Amber
!: TCP refused.
Successful latency uses ▁▂▃▄▅▆▇█.
The fixed mapping keeps rows comparable:
| Latency | Glyph |
|---|---|
<=1ms |
▁ |
<=3ms |
▂ |
<=10ms |
▃ |
<=30ms |
▄ |
<=100ms |
▅ |
<=300ms |
▆ |
<=1000ms |
▇ |
>1000ms |
█ |
Use --history-style dots, --history-style sparkline, or --history-style none when a terminal font or workflow needs a simpler display.
Host States
pinghue classifies each host from the whole run, not just the most recent probes. Once a host has replied successfully, down requires --fail-threshold consecutive failures. A run with no successful replies is down immediately, even if it ends before the threshold. Any packet loss, or any observed jitter above --jitter-threshold, latches the host as intermittent until you reset it (r or R in the TUI). The reported jitter_ms remains the current decaying RFC 3550 estimate, so it can later fall below the threshold while the whole-run status stays intermittent. This is intentional: a maintenance-window report should reflect everything that happened, not only the final moments.
Slate + Signal Palette
pinghue uses a low-glare Slate + Signal palette designed for long maintenance windows: dark structure, high-contrast text, and saturated status colors that make the affected metric stand out quickly.
| Role | Hex | Used for |
|---|---|---|
| Background | #101418 |
Terminal body and empty space. |
| Panel | #151b22 |
Table surface and primary content areas. |
| Header | #1b2630 |
Title bars and footer bands. |
| Border | #2a313a |
Table outlines and separators. |
| Text | #e6edf3 |
Normal readable values. |
| Muted | #8ea0b8 |
Labels, secondary text, and inactive chrome. |
| Green | #7ee787 |
Healthy state, successful probes, and normal history bars. |
| Amber | #f2cc60 |
Slow latency, high jitter, intermittent state, and TCP refused markers. |
| Red | #ff7b72 |
Loss, down state, timeouts, and failed history markers. |
| Selection Blue | #58a6ff |
Focus accents and selected-row treatment. |
Doctor
Run the doctor before relying on ICMP mode:
pinghue --check
Linux may block unprivileged ICMP sockets. TCP mode does not need special privileges:
pinghue -p 443 example.com
If pinghue --check reports that your GID is outside net.ipv4.ping_group_range, the preferred fix is to allow only your current group:
gid="$(id -g)"
sudo sysctl -w "net.ipv4.ping_group_range=${gid} ${gid}"
echo "net.ipv4.ping_group_range=${gid} ${gid}" \
| sudo tee /etc/sysctl.d/99-pinghue.conf
The broader range 0 2147483647 also works, but enables unprivileged ICMP for every local group on the system.
setcap cap_net_raw does not work for pip or Homebrew installs: the pinghue command is a Python launcher script, and Linux ignores file capabilities on interpreter scripts. Use the ping_group_range fix above, or TCP mode. Do not set capabilities on a shared Python interpreter.
JSON Output
--output PATH writes one JSON document per run. Existing regular files are preserved by default; add --overwrite when replacing a known report path is intentional. Symlinks, sockets, multiply linked regular files, and other unsupported special nodes are never replaced. Existing character devices and FIFOs are written directly only when they can be opened safely as the same node. This is the breaking 2.0.0 CLI change for scripts that previously reused the same output file. The schema lives at schemas/output-v1.schema.json, and an example lives at examples/pinghue-output-example.json.
New report creation is no-clobber and first tries to hard-link a complete temporary report into place atomically. On filesystems without hardlink support (for example exFAT and some FUSE mounts), it falls back to an exclusive create and copy: ordinary errors and handled interrupts remove that fallback file, but an abrupt process or system failure can leave a partial new report. Explicitly overwriting an existing regular file uses a descriptor-verified in-place rewrite so a path race cannot replace a symlink, socket, FIFO, or device node; that path is also not crash-atomic. Evidence workflows should accept a report only after PingHue exits successfully and the JSON parses, then rotate it as a separate step.
pinghue -f hosts.txt --duration 180 --output maintenance.json
pinghue -f hosts.txt --duration 180 --output maintenance.json --overwrite
Use --no-samples when you only need final per-target statistics.
Every output document includes:
schema_versionpinghue_version- run metadata (including
samples_window) - probe configuration
- ordered target results
- per-target stats
- optional per-probe samples
Per-target stats (sent, received, loss, latency, jitter) are computed over every probe in the run, so stats.sent reflects the whole run. The per-target samples array retains only the most recent run.samples_window probes. The window is at most 1000 per target and is reduced uniformly when needed to keep the run-wide retained tail within 100,000 samples. On long runs stats.sent therefore exceeds len(samples) — that is expected windowing, not a truncated file. When reconciling evidence, treat samples as the recent tail and stats as authoritative for the full run; --no-samples emits an empty array for every target.
The run.host field defaults to local to avoid leaking workstation hostnames. Use --host-label when a report needs an operator-selected system or maintenance-window label. Operator-visible target, host-label, and error text is escaped outside printable ASCII so terminal controls and visually deceptive Unicode are represented literally.
Security Model
pinghue is a local CLI/TUI. It does not run a server, accept remote requests, store credentials, or require secrets. The security-sensitive areas are:
- terminal rendering of operator-supplied hostnames and OS error strings
- Linux ICMP privilege configuration
- local output paths selected by the operator
- release workflow integrity
See pinghue-threat-model.md, security-best-practices-report.md, and SECURITY.md.
Release Channels
Published release channels:
- GitHub repository:
inxbit/pinghue - GitHub Actions CI on Linux and macOS
- GitHub Release with sdist and wheel artifacts
- PyPI trusted publishing through a protected
pypienvironment - Homebrew tap:
inxbit/tapfrominxbit/homebrew-tap
The release workflow is tag-driven, but only after the release PR has merged.
Create the signed annotated tag from the merged public main commit:
git fetch --prune origin
git tag -s vX.Y.Z -m "Release vX.Y.Z" "$(git rev-parse origin/main)"
git tag -v vX.Y.Z
git push origin vX.Y.Z
Development
Contributor setup, hash-pinned dependency audits, tests, and build verification are documented in the repository's CONTRIBUTING.md. Those maintainer tools are intentionally not part of the installed package.
License
MIT. See LICENSE.
Release files for pinghue 5.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pinghue-5.0.0.tar.gz | 1.3 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pinghue-5.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.3 MB
Release files / pinghue-5.0.0.tar.gz
| Download URL | pinghue-5.0.0.tar.gz |
|---|---|
| Size | 1.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
33c0adb9985ea5fc737f67857435719193923bab3869d4c63c9b7f46adf696bb
|
|
BLAKE2b-256 checksum How to use checksums |
a9cc326dbaaf5ce7f2421eabb64c68fe8620dc33bc65663cef1bd95da8b4772f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 11, 2026.
Transparency logRelease files / pinghue-5.0.0-py3-none-any.whl
| Download URL | pinghue-5.0.0-py3-none-any.whl |
|---|---|
| Size | 44.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3a1339669a70ffb5c779f0be988b9fd18fb02c0297e328e8b8a6f36a138c7a4f
|
|
BLAKE2b-256 checksum How to use checksums |
f79115605863559419e62562be7175ae3789d26efb11c786561812608f55c1f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 11, 2026.
Transparency log