Metadata-Version: 2.4
Name: ipmg
Version: 1.13.0
Summary: IP Management & Ping Monitoring CLI Tool
Author: Sameer Alam
Maintainer-email: Sameer Alam <sameeralam3127@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/sameeralam3127/ipmg
Project-URL: Documentation, https://github.com/sameeralam3127/ipmg#readme
Project-URL: Repository, https://github.com/sameeralam3127/ipmg
Project-URL: Issues, https://github.com/sameeralam3127/ipmg/issues
Project-URL: Changelog, https://github.com/sameeralam3127/ipmg/releases
Keywords: ip,network,ping,monitoring,cli,port-scan
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet
Classifier: Topic :: System :: Networking
Classifier: Topic :: System :: Monitoring
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Information Technology
Classifier: Operating System :: OS Independent
Classifier: Environment :: Console
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pandas>=2.0
Requires-Dist: openpyxl>=3.1
Requires-Dist: rich>=13.0
Requires-Dist: fastapi>=0.110
Requires-Dist: uvicorn>=0.27
Requires-Dist: websockets>=12
Requires-Dist: python-multipart>=0.0.9
Provides-Extra: dev
Requires-Dist: pytest>=7.4; extra == "dev"
Requires-Dist: black>=24.0; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: pre-commit>=3.5.0; extra == "dev"
Requires-Dist: python-semantic-release>=10.5.3; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Dynamic: license-file

# IPMG — IP Management & Ping Monitoring Tool

[![PyPI](https://img.shields.io/pypi/v/ipmg)](https://pypi.org/project/ipmg/)
![Python Version](https://img.shields.io/badge/python-3.9%2B-blue)
![License](https://img.shields.io/badge/license-MIT-green)
[![Publish](https://github.com/sameeralam3127/ipmg/actions/workflows/publish.yml/badge.svg)](https://github.com/sameeralam3127/ipmg/actions/workflows/publish.yml)

Scan and monitor IP networks from the command line — or from a local web
dashboard. IPMG pings hosts in parallel, resolves hostnames, and exports
results as Excel, CSV, JSON, or Markdown reports.

> **Security note:** only scan networks you are authorized to scan.
> Unauthorized scanning may violate your organization's policies or the law.

---

## Install

```bash
pip install ipmg
```

Check that it works:

```bash
ipmg --version
```

<details>
<summary>Other install methods</summary>

**uv (isolated global install):**

```bash
uv tool install ipmg
```

**curl installer (installs uv if missing, then installs/upgrades ipmg from PyPI):**

```bash
curl -sSL https://raw.githubusercontent.com/sameeralam3127/ipmg/main/install.sh | bash
```

**From source (development):**

```bash
git clone https://github.com/sameeralam3127/ipmg.git
cd ipmg
pip install -e .
```

</details>

---

## Quick start

```bash
# Scan your local subnet automatically
ipmg --discover

# Scan a single host
ipmg --input 8.8.8.8

# Scan a CIDR range
ipmg --input 192.168.1.0/24

# Scan an IP range
ipmg --input 10.0.0.1-10.0.0.50

# Scan targets from a file (xlsx, csv, txt, or list)
ipmg --input targets.txt

# Resolve hostnames and export CSV + a readable Markdown report
ipmg --input targets.txt --resolve --formats md csv

# Watch hosts appear as they answer, instead of waiting for the summary
ipmg --input 192.168.1.0/24 --stream

# Scan and report what changed since the previous scan
ipmg --input targets.txt --compare

# List stored scans, then compare any two of them
ipmg history
ipmg diff 12 14

# Open the local web dashboard
ipmg dashboard
```

Running plain `ipmg` uses `ip_list.xlsx` as input and creates a sample file
if it does not exist.

---

## Live results

By default a scan prints its results once every host has been probed. On a
large range that is a long wait with nothing to look at, so `--stream`
prints each host the moment its probe finishes, above a progress bar that
also carries a running count of the hosts that answered:

```bash
ipmg --input 192.168.1.0/24 --stream
```

```
  Live
  Status        Host                Latency
  ● Active      192.168.1.1          0.9 ms
  ● Active      192.168.1.24         3.1 ms
   ⠹ Scanning ━━━━━━━━━━━───────────  48% 122/254 0:00:09 2 up
```

`--stream` shows only the hosts that answer, which is what makes a sparse
range readable. Add `--stream-all` to see every result, including timeouts
and unreachable hosts. The rows gain a `Name` column under `--resolve` and
an `Open ports` column under `--scan-ports`.

Streaming costs nothing in scan time: rows are printed by the thread that
collects results, so the workers never wait on the terminal. When output is
piped or redirected the progress bar is dropped and the rows are written as
plain lines, which makes `ipmg --stream-all >> scan.log` a usable live log.

---

## Change detection

Every scan is stored in a local SQLite history (`~/.ipmg/dashboard.db`),
shared by the CLI and the dashboard. IPMG can then tell you what moved
between any two scans.

```bash
ipmg --input targets.txt --compare                   # compare with the previous scan
ipmg diff                                            # compare the two latest scans
ipmg diff 14                                         # compare scan 14 with the one before it
ipmg diff 12 14                                      # compare two specific scans
ipmg diff --diff-formats md json                     # export the change summary
ipmg diff --fail-on-change                           # exit 2 when anything changed (CI)
ipmg history --limit 10                              # list stored scans
```

Detected changes:

| Change | Severity | Meaning |
| --- | --- | --- |
| Host offline | critical | Reachable in the baseline, not reachable now |
| New host | warning | An IP that the baseline never saw |
| Host removed | warning | An IP the current scan no longer covers |
| IP address changed | warning | A known hostname moved to a different IP |
| Service changed | warning | Status moved between failure modes (e.g. `Timeout` → `Unreachable`) |
| Host back online | info | Recovered since the baseline |
| Hostname changed | info | Same IP, different PTR record |
| Latency changed | info | Latency moved past both thresholds |

A latency change is only reported when it clears **both** `--latency-threshold`
(default 5 ms) and `--latency-pct` (default 25%), which keeps normal jitter out
of the report.

| Flag | Default | Description |
| --- | --- | --- |
| `--compare` | off | Print a change report after the scan |
| `--compare-any-source` | off | Allow a baseline from a different target source |
| `--no-history` | off | Do not store the scan |
| `--db` | `~/.ipmg/dashboard.db` | History database location |
| `--diff-formats` | none | Export the change summary as `md`, `json`, `csv` |
| `--diff-output` | `changes` | Base filename for exported change summaries |
| `--latency-threshold` | `5` | Minimum latency delta in ms |
| `--latency-pct` | `25` | Minimum relative latency change |
| `--fail-on-change` | off | `ipmg diff` exits 2 when changes are found |

By default a scan is compared against the previous scan **of the same target
source**, so file-based and `--discover` runs do not get mixed up.

---

## Web dashboard

```bash
ipmg dashboard          # starts http://127.0.0.1:8080 and opens your browser
```

A modern browser UI that shares the CLI's scanning engine and runs fully
offline — every stylesheet and script is bundled with the package, nothing
is loaded from a CDN. It gives you:

- **Dashboard** — status donut, latency trend, and recent scan overview
- **New Scan** — upload Excel/CSV/text/JSON target files or type IPs,
  CIDR blocks, and ranges; configure threads, timeout, and DNS options
- **Live Monitor** — real-time progress and results over WebSockets
- **History** — every scan stored locally in SQLite (`~/.ipmg/dashboard.db`),
  searchable and downloadable as XLSX/CSV/JSON/Markdown
- **Changes** — pick any two scans and see new/offline hosts, IP and hostname
  moves, and latency shifts; export the summary as Markdown/JSON/CSV
- **Inventory** — every host seen across scans, with last status and export

| Flag | Default | Description |
| --- | --- | --- |
| `--port` | `8080` | Port to listen on |
| `--host` | `127.0.0.1` | Bind address (local-only by default) |
| `--no-browser` | off | Don't open the browser automatically |
| `--db` | `~/.ipmg/dashboard.db` | History database location |

`ipmg web` is an alias for `ipmg dashboard`.

### Headless / remote servers

On a Linux server with no display (e.g. accessed over plain SSH), IPMG
detects that no browser can be opened, skips the attempt, and prints a hint
instead of failing silently. The dashboard still binds to `127.0.0.1` by
default, so reach it from your workstation with an SSH tunnel:

```bash
ssh -L 8080:127.0.0.1:8080 user@server
# then open http://127.0.0.1:8080 locally
```

Alternatively, bind to all interfaces with `--host 0.0.0.0` — this exposes
an unauthenticated API on the network, so only do this on a trusted network
or behind a reverse proxy with authentication (see [Security](#security)).

---

## Options

| Flag | Default | Description |
| --- | --- | --- |
| `--input` | `ip_list.xlsx` | Targets: a file (`.xlsx`, `.xls`, `.csv`, `.txt`, `.list`), a single IP, a CIDR block, or an IP range (`10.0.0.1-10.0.0.50`) |
| `--output` | `results` | Output file name prefix |
| `--formats` | `xlsx` | One or more of `xlsx`, `csv`, `json`, `md` |
| `--discover` | off | Auto-detect and scan the local subnet |
| `--resolve` | off | Reverse DNS (PTR) lookup for each host |
| `--dns-cache-ttl` | `300` | Cache DNS results for this many seconds (`0` disables caching) |
| `--timeout` | `2` | Ping timeout in seconds |
| `--count` | `1` | Pings per host |
| `--threads` | `50` | Parallel workers |
| `--scan-ports` | off | Probe common TCP ports on hosts that answer ICMP |
| `--ports` | `21,22,25,53,80,443,445,1433,3306,3389,5432` | Comma-separated TCP ports to probe when `--scan-ports` is set |
| `--port-timeout` | `1` | Connect timeout per port in seconds, when `--scan-ports` is set |
| `--stream` | off | Print each host that answers as soon as its probe finishes |
| `--stream-all` | off | Stream every result, including hosts that did not answer (implies `--stream`) |
| `--stream-refresh` | `0.25` | Seconds between progress-bar redraws while streaming (0.05-5) |
| `--interval` | off | Repeat the scan every N minutes |
| `--compare` | off | Report what changed since the previous scan |
| `--no-history` | off | Do not store the scan in the history database |
| `--verbose` | off | Debug logging |

Exit codes: `0` success, `1` error, `2` changes detected
(`ipmg diff --fail-on-change`), `130` interrupted.

---

## Input formats

- **Excel / CSV** — must contain an `IP Address` column:

  | IP Address  |
  | ----------- |
  | 192.168.1.1 |
  | 10.0.1.0/30 |

- **Text file** — one IP or CIDR per line; blank lines and `#` comments are ignored:

  ```text
  # Production DNS
  8.8.8.8
  192.168.1.0/30
  ```

- **Command line** — a literal IP (`8.8.8.8`), CIDR block (`10.0.0.0/24`),
  or IP range (`10.0.0.1-10.0.0.200`)

Duplicate targets are removed automatically, and one scan expands to at most
65,536 hosts — larger CIDR blocks or ranges are rejected up front.

---

## Output

```text
  ipmg 1.8.1  ·  scan
  ICMP probes only — scan only networks you are authorized to scan.

  Source   targets.txt
  Targets  3 hosts
  Config   50 threads · 2s timeout · 1 ping · reverse DNS

  Results
  ● Active   2  ━━━━━━━━━━━━━━━───────  66.7%
  ● Timeout  1  ━━━━━━━───────────────  33.3%

  3 hosts · 66.7% active · 5.8 ms avg · 3.08s · 2026-07-26 23:13:25

  Saved    results_20260726_231328.csv
```

Colors follow `NO_COLOR`, the progress bar is hidden when output is piped,
and the symbols fall back to ASCII on terminals that cannot render them.

Result files (e.g. `results_20260628_120000.xlsx`) contain one row per host:

| IP Address | Status | Latency | Hostname   | Open Ports | Batch Timestamp     | Scan Duration (s) |
| ---------- | ------ | ------- | ---------- | ---------- | ------------------- | ----------------- |
| 8.8.8.8    | Active | 12.5    | dns.google | 443        | 2026-04-09 11:42:13 | 6.24               |

Status values: `Active`, `Inactive`, `Timeout`, `Unreachable`, `Invalid IP`, `Error`.

`Open Ports` is only populated when `--scan-ports` is set: for each host that
answers ICMP, IPMG probes a configurable list of common TCP ports
(SSH, HTTP, HTTPS, RDP, SMB, FTP, SMTP, DNS, MSSQL, MySQL, PostgreSQL by
default) concurrently and records which ones accepted a connection.

```bash
ipmg --input targets.txt --scan-ports
ipmg --input targets.txt --scan-ports --ports 22,80,443 --port-timeout 0.5
```

The `md` format produces a shareable Markdown report with a status summary
table — handy for tickets, handoffs, and incident timelines.

---

## Security

IPMG is built for scanning networks you are authorized to scan, and the
tool itself is hardened accordingly:

- Pings run as a direct process call (no shell), and every target is
  validated as an IP address first
- The dashboard binds to `127.0.0.1` by default and serves everything
  locally — no CDN assets, no outbound requests
- WebSocket connections are origin-checked, so a web page you happen to
  visit cannot connect to the local dashboard and read your scan results
- Uploads are capped at 5 MB and one scan expands to at most 65,536 hosts,
  so a bad input file cannot exhaust memory
- All database access uses parameterized SQL

If you bind to a non-local address with `--host`, anyone who can reach that
interface can start scans and read results — put a reverse proxy with
authentication in front of it.

Found a vulnerability? See [SECURITY.md](SECURITY.md) for how to report it.

---

## Troubleshooting

- **`command not found: ipmg`** — make sure pip's script directory is on your
  `PATH`, or reinstall with `pip install ipmg`.
- **Hostname shows `Unresolvable`** — the host has no DNS PTR record.
- **Input file rejected** — check the extension is supported and that
  spreadsheets/CSVs include an `IP Address` column.

---

## Development

```bash
pip install -e ".[dev]"
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest -q
```

Releases are automated: merging a conventional commit (`feat: ...`,
`fix: ...`) to `main` triggers GitHub Actions to run tests, create a
semantic-release tag, and publish to [PyPI](https://pypi.org/project/ipmg/).

### GitHub Pages demo

The dashboard also has a static, interactive demonstration at
[sameeralam3127.github.io/ipmg](https://sameeralam3127.github.io/ipmg/).
GitHub Pages cannot run the Python scanner or access a local SQLite database,
so this version transparently uses realistic seeded network inventory and scan
history. Search, filters, comparison, exports, theme switching, and a manual
demo scan all work in the browser. The local `ipmg dashboard` command always
uses the real FastAPI API and scan engine instead.

The `Deploy dashboard demo to GitHub Pages` workflow publishes
`src/ipmg/web/static` after changes to `main`. In repository settings, select
**GitHub Actions** as the GitHub Pages source once; no secrets are required.

To review the static experience locally, run any static web server from
`src/ipmg/web/static` and open it with `?demo=1`:

```bash
cd src/ipmg/web/static
python3 -m http.server 4173
# http://127.0.0.1:4173/?demo=1
```

---

## License

MIT — free for commercial and personal use.
