Metadata-Version: 2.4
Name: mb-netwatch
Version: 0.2.3
Summary: macOS internet connection monitor
Project-URL: Homepage, https://github.com/mcbarinov/mb-netwatch
Project-URL: Repository, https://github.com/mcbarinov/mb-netwatch
Author: mcbarinov
License-Expression: MIT
License-File: LICENSE
Keywords: latency,macos,menubar,monitor,network,vpn
Classifier: Operating System :: MacOS
Classifier: Topic :: System :: Networking :: Monitoring
Classifier: Topic :: Utilities
Requires-Python: >=3.14
Requires-Dist: aiohttp~=3.13.5
Requires-Dist: dnspython~=2.8.0
Requires-Dist: mm-clikit~=0.2.0
Requires-Dist: mm-pymac~=0.0.3
Requires-Dist: psutil~=7.0
Description-Content-Type: text/markdown

# mb-netwatch

macOS internet connection monitor. Tracks warm + cold latency, VPN status, and public IP at a glance via a menu bar icon.

> **Status:** Under active development.

## What it monitors

Five types of checks run continuously in the background:

- **Latency (warm)** — HTTP probes over a reused keep-alive session, every 2 seconds (steady-state probe — first response wins)
- **Latency (cold)** — HTTP probes over a fresh session (full TCP+TLS setup), every 10 seconds (catches connection-setup failures a warm probe hides)
- **DNS** — UDP query to every system resolver in parallel, every 10 seconds (reachability + cached-answer speed of the resolvers macOS is actually using right now)
- **VPN status** — tunnel interface and routing table detection every 10 seconds
- **Public IP** — address and country code via plain-text IP services every 60 seconds

See [docs/probes.md](docs/probes.md) for detailed algorithms, endpoints, and design rationale.

## CLI commands

- `mb-netwatch` — TUI dashboard (default when no command given)
- `mb-netwatch probe` — one-shot connectivity probe, print result
- `mb-netwatch probed` — run continuous background measurements
- `mb-netwatch tray` — run menu bar UI process
- `mb-netwatch start` — start probed and tray in the background
- `mb-netwatch stop` — stop probed and tray
- `mb-netwatch diagnose dns` — on-demand extended DNS diagnostic with verdict (see [docs/diagnostics.md](docs/diagnostics.md))
- `mb-netwatch raycast install` — install Raycast Script Commands (see [docs/raycast.md](docs/raycast.md))

## Architecture

General CLI application architecture patterns are described in [docs/cli-architecture.md](docs/cli-architecture.md). Database schema and storage rules are in [docs/db.md](docs/db.md). Below is the project-specific structure.

### Core (`core/`)

Central application layer. Holds database, business logic, probe implementations, and on-demand diagnostics. Consumers never import from `core/` directly — they receive a `Core` instance and access everything through it:

- `core.db` — database (reads and writes)
- `core.config` — application configuration
- `core.service` — business logic (running probes, storing results)

Two distinct kinds of network checks live under `core/`:

- **`core/probes/`** — small, lightweight measurements run continuously by `probed` and stored to SQLite. Each probe is a single round-trip with no retries, by design. Results drive the tray icon, the TUI dashboard, and historical sparklines. See [docs/probes.md](docs/probes.md).
- **`core/diagnostics/`** — heavier, on-demand checks invoked by hand when something looks broken. They may run multiple transports in parallel, query external comparators, and produce an interpreted verdict. Results are returned to the caller and never stored. See [docs/diagnostics.md](docs/diagnostics.md).

### Consumers

Three independent consumers of `Core`:

- **CLI** (`cli/`) — command-line interface. Each command receives `Core` and `Output` via `CoreContext`.
- **TUI** (`tui/`) — Textual terminal dashboard. Polls `core.db` for latest results and renders a live view with warm and cold latency sparklines, VPN/IP status, and recent events.
- **Daemon** (`daemon.py`) — long-running background process. Orchestrates scheduling (loops, timers, signals) and delegates all probe/store logic to `core.service`.
- **Tray** (`tray.py`) — macOS menu bar UI. Polls `core.db` for latest results and updates the icon.

### Processes

Two long-running processes in normal operation:

- **probed** (`mb-netwatch probed`) — runs the daemon; measures warm latency every 2 s, cold latency every 10 s, VPN status every 10 s, public IP every 60 s; writes to SQLite via `core.service`.
- **tray** (`mb-netwatch tray`) — UI only; reads latest samples from SQLite via `core.db`, updates menu bar icon and dropdown.

The tray must not perform network probing directly. This separation keeps UI responsive and simplifies debugging.

## Storage

SQLite database at `~/.local/mb-netwatch/netwatch.db` (WAL mode).
See [docs/db.md](docs/db.md) for schema, deduplication strategy, and retention rules.

## Configuration

Optional TOML config at `~/.local/mb-netwatch/config.toml`. The file is not created automatically — create it only if you want to override defaults. All keys are optional — only specify what you want to change.

```toml
[probed]
warm_latency_interval = 2.0    # seconds between warm-latency probes (default: 2.0)
cold_latency_interval = 10.0   # seconds between cold-latency probes (default: 10.0)
vpn_interval = 10.0            # seconds between VPN status checks (default: 10.0)
ip_interval = 60.0             # seconds between public IP lookups (default: 60.0)
dns_interval = 10.0            # seconds between DNS probes (default: 10.0)
purge_interval = 3600.0        # seconds between old-data purge runs (default: 3600.0)
warm_latency_timeout = 5.0     # HTTP timeout for warm-latency probes (default: 5.0)
cold_latency_timeout = 5.0     # HTTP timeout for cold-latency probes (default: 5.0)
ip_timeout = 5.0               # HTTP timeout for IP/country lookups (default: 5.0)
dns_timeout = 2.0              # per-resolver UDP query timeout for DNS probes (default: 2.0)
retention_days = 30            # days to keep raw rows before purging (default: 30)

[warm_latency_threshold]
ok_ms = 300              # warm latency below this → OK (default: 300)
slow_ms = 800            # warm latency below this → SLOW, at or above → BAD (default: 800)
stale_seconds = 10.0     # seconds before warm data is considered stale (default: 10.0)

[cold_latency_threshold]
ok_ms = 600              # cold latency below this → OK (default: 600)
slow_ms = 1500           # cold latency below this → SLOW, at or above → BAD (default: 1500)
stale_seconds = 30.0     # seconds before cold data is considered stale (default: 30.0)

[dns_threshold]
stale_seconds = 30.0     # seconds before DNS data is considered stale (default: 30.0)

[tray]
poll_interval = 2.0      # seconds between tray DB polls (default: 2.0)

[tui]
poll_interval = 0.5            # seconds between TUI dashboard DB polls (default: 0.5)
sparkline_history_max = 300    # max latency readings drawn per sparkline (default: 300)
```

The menu bar shows a fixed-width 3-character title: 2-letter country code + status symbol (`●` OK / `◐` SLOW / `○` BAD / `✕` DOWN), e.g. `US●`. Click the menu bar icon to see the exact latency in the dropdown. If probed stops writing data, the symbol changes to `–` (en dash) after `stale_seconds` seconds (default 10). While waiting for the first data, a middle dot `·` is displayed.

## Installation

```
uv tool install mb-netwatch
mb-netwatch start
```

## Tech stack

- Python 3.14
- [aiohttp](https://docs.aiohttp.org/) — HTTP probes
- [psutil](https://github.com/giampaolo/psutil) — network interface inspection for VPN detection
- [mm-clikit](https://github.com/mcbarinov/mm-clikit) — CLI toolkit (Typer enhancements, SQLite, process management)
- [mm-pymac](https://github.com/mcbarinov/mm-pymac) — macOS menu bar app
