Metadata-Version: 2.4
Name: isitup-cli
Version: 0.1.3
Summary: A small CLI that periodically checks whether servers respond to ping, HTTP, and TCP ports
Author: stefan.insam
Author-email: stefan.insam <stefan.insam@netgo.de>
License-Expression: GPL-3.0-only
License-File: LICENSE
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: Topic :: System :: Networking :: Monitoring
Classifier: Topic :: Utilities
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: requests>=2.34.2
Requires-Dist: rich>=15.0.0
Requires-Python: >=3.14
Project-URL: Repository, https://github.com/ramsesoriginal/isitup
Project-URL: Issues, https://github.com/ramsesoriginal/isitup/issues
Description-Content-Type: text/markdown

<div align="center">
  <img src="https://raw.githubusercontent.com/ramsesoriginal/isitup/main/logo/main_hero.svg" alt="isitup" width="720">

  <p><strong>Is it up? Ping, HTTP, and TCP checks in one small, fast CLI.</strong></p>

  [![PyPI](https://img.shields.io/pypi/v/isitup-cli?color=blue)](https://pypi.org/project/isitup-cli/)
  [![CI](https://github.com/ramsesoriginal/isitup/actions/workflows/ci.yml/badge.svg)](https://github.com/ramsesoriginal/isitup/actions/workflows/ci.yml)
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](LICENSE)
  [![Python 3.14+](https://img.shields.io/badge/python-3.14%2B-3776AB?logo=python&logoColor=white)](pyproject.toml)
  [![uv](https://img.shields.io/badge/uv-managed-DE5FE9?logo=uv&logoColor=white)](https://github.com/astral-sh/uv)
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
  [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
  [![GitHub stars](https://img.shields.io/github/stars/ramsesoriginal/isitup?style=social)](https://github.com/ramsesoriginal/isitup)
</div>

---

**isitup** is a small CLI that periodically checks whether servers respond to ping and,
depending on how the target is written, HTTP or a TCP port:

| Target | Checks |
| --- | --- |
| `example.com` | ping only |
| `example.com:1337` | ping + TCP connect to port 1337 |
| `http://example.com` / `https://example.com` | ping + HTTP (catching a 500, a DNS failure, an expired TLS certificate, a redirect loop, etc.) |
| `https://example.com:1337` | ping + HTTP, against that port |

Ping is checked independently from the HTTP/TCP probe: many hosts (behind
load balancers, CDNs, or firewalls) drop ICMP but serve their actual service
just fine, so a blocked ping alone doesn't mark a target down: the "Status"
column is based on the probe (HTTP or TCP), with ping shown alongside as
extra diagnostic info. A target only shows as `OFFLINE` when both ping and
the probe fail; a ping-only target's status is based on ping alone, since
there's nothing else to check.

If the same host appears more than once (e.g. `example.com` and
`https://example.com/health`), it's only pinged once per round. The ping
result is shared across all targets pointing at that host.

While checks are running you get a live spinner and a progress bar per
target counting down its timeout; results are then shown in a table that
updates in place (via [`rich`](https://github.com/Textualize/rich)) rather
than reprinting on every round.

Each target also keeps a rolling window of its last 20 response times for
the session (HTTP response time or TCP connect time, whichever applies),
shown as Min/Max latency columns and a tiny sparkline ("Trend"), along with
the last time it was seen online and the last time it was seen offline.
This history is in-memory only and resets each time you start the tool.

## Installation

The package is published on PyPI as `isitup-cli`, but it installs a plain
`isitup` command:

```bash
uv tool install isitup-cli
# or: pipx install isitup-cli
# or: pip install isitup-cli
```

To try unreleased changes straight from `main` instead (no PyPI release
needed):

```bash
uv tool install git+https://github.com/ramsesoriginal/isitup
```

Either way, remove it later with `uv tool uninstall isitup`.

## Usage

```bash
isitup --url example.com --url https://example.org --url db.internal:5432
```

Or with a config file:

```bash
cp config.example.yaml config.yaml
# edit config.yaml
isitup --config config.yaml
```

Config files and `--url` flags can be combined; the tool watches the union of
both. The config file is watched for changes and reloaded automatically:
add, remove, or edit targets without restarting the tool (`--url` targets
can't change at runtime, only what's in the file).

### Options

| Flag | Description |
| --- | --- |
| `-c`, `--config PATH` | YAML file listing targets; reloaded automatically on change |
| `-u`, `--url TARGET` | a target to monitor (repeatable): hostname, `hostname:port`, or a URL |
| `-i`, `--interval SECONDS` | seconds between check rounds (default: 30) |
| `--ping-timeout SECONDS` | ping reply timeout (default: 2) |
| `--http-timeout SECONDS` | timeout for the HTTP request or TCP connect (default: 5) |
| `--no-ping` | skip ICMP entirely, HTTP/TCP-only checks |
| `--once` | run a single round and exit; exit code is `1` if any target is down, `0` otherwise. Useful as a cron/CI health gate |
| `--plain` | print one plain-text line per target per round instead of the live UI (for logging/piping) |
| `--json` | print one JSON object per target per round instead of the live UI (for machine consumption) |

`--plain` and `--json` are mutually exclusive with each other, and both skip
the interactive spinner/progress UI entirely.

### Note on ping

Some servers/networks (common on cloud providers, behind load balancers or
CDNs) block ICMP echo requests entirely even though the service itself is
perfectly reachable. The "Status" column already accounts for this and
won't flag such a target as down (unless it's ping-only, in which case ping
*is* the only signal there is). If you'd rather not run ping checks against
a target at all (e.g. to skip the ~2s ping timeout), either set `ping: false`
for it in the config file, or pass `--no-ping` to disable ICMP checks
globally. Note that a ping-only target (bare hostname, no port) can't have
`ping: false`, there would be nothing left to check.

### Failure classification

When the HTTP or TCP probe fails, the specific reason is captured as
`error_kind` (visible in `--json` output, and shown as a short tag like
`DOWN (DNS)` in the Service column otherwise):

| `error_kind` | Meaning |
| --- | --- |
| `dns` | the hostname failed to resolve |
| `tls` | a TLS/certificate error (HTTP only, e.g. expired or self-signed cert) |
| `redirect` | too many redirects (HTTP only, possible redirect loop) |
| `timeout` | didn't complete within `--http-timeout` |
| `connection` | connection refused or otherwise unreachable |
| `client_error` / `server_error` | got a 4xx / 5xx HTTP response |
| `request_error` | anything else `requests` raised |

## Development

```bash
uv sync
uv run isitup --once --url https://example.com

uv run pytest       # tests
uv run ruff check .    # lint
uv run ruff format .   # format
uv run mypy src tests  # type check
```

Every push and pull request runs this same lint/type-check/test suite via
[GitHub Actions](.github/workflows/ci.yml), across Linux and Windows.

## License

[GPL-3.0](LICENSE): see [LICENSE](LICENSE) for the full text.
