bilingual readme and architecture docs

This commit is contained in:
Andrew Pimenov 2026-08-08 21:21:29 +03:00
parent de09864eba
commit 2a3020cdb9
4 changed files with 371 additions and 2 deletions

91
ARCHITECTURE.md Normal file
View file

@ -0,0 +1,91 @@
# Architecture
[Русский](ARCHITECTURE.ru.md)
netcheck collects network facts from many independent, mostly keyless sources, merges them into typed structures, interprets them with pure functions, and renders two artifacts per run. Collection, interpretation and rendering never mix: every module either gathers data or reasons about already-gathered data, never both.
## Directory layout
```
src/netcheck/
cli.py Typer app: flags, phase orchestration, Rich progress. No business logic.
config.py pydantic-settings loader. Precedence: env > .env > config.yaml > default.
models.py Every shared dataclass, plus to_jsonable() for serialization.
orchestration.py run_module(): timing, timeout, exception -> ProbeError classification.
netinfo.py Capability detection and local interface/gateway/MTU/resolver facts.
ip_geo.py Six-provider identity chain, per-provider normalizers, field-wise merge.
bgp.py RIPEstat, CAIDA ASRank, Team Cymru DNS, PeeringDB (disk-cached).
reputation.py FireHOL netset cache + prefix lookup, Shodan InternetDB, DNSBL decode.
stats.py Pure RTT statistics: loss, min/avg/max, mdev, jitter.
traceparse.py Pure text -> TraceHop parsers for Windows, Linux and BSD/macOS output.
interpret.py Pure verdict engine: thresholds -> Finding[], VPN scoring, bufferbloat grade.
speed.py Cascading speedtest, throughput math, cfL4 header parsing, bufferbloat probe.
exporter.py JSON and Markdown rendering, atomic writes into ./logs/.
compare.py --compare: diff two saved JSON reports.
watch.py --watch: periodic re-run loop with a live Rich dashboard.
probes/
latency.py Ping fan-out and jitter/loss/mdev statistics.
traceroute.py Cascade: mtr --json -> icmp_win -> icmplib -> system binary text.
icmp_win.py ctypes IcmpSendEcho2 / Icmp6SendEcho2 engine (Windows only).
dns_leak.py Per-adapter resolver enumeration, echo probes, ECS detection.
```
## Data flow
```
+-- netinfo (local facts, capabilities)
|
cli.py -------+-- ip_geo provider chain ---> egress IP + ASN [blocking: everything downstream needs the ASN]
|
+-- bgp || reputation || dns_leak (parallel)
|
+-- latency || traceroute fan-out (parallel, semaphore-bounded)
|
+-- speed (exclusive phase lock)
|
+-- interpret ---> Finding[] + overall verdict
|
+-- exporter ---> logs/report_<ASN>_<ts>.md + .json
```
Two hard concurrency constraints:
1. **Measurement isolation.** The speedtest never overlaps traceroute, ping or API calls — it holds an exclusive phase lock. The single intentional exception is the bufferbloat probe, which overlaps saturation by design, inside `speed.py`.
2. **Bounded traceroute fan-out.** Parallel traceroutes to different targets share early hops and inflate each other's latency, so concurrent traces are capped by a semaphore (`probing.trace_concurrency`, default 2).
## Key decisions
**Failure is a value, not control flow.** Every module call goes through `run_module()`, which times it, enforces a per-module timeout, catches everything except `CancelledError`, and returns a `ModuleResult` with `status` in `ok | partial | failed | skipped`. `asyncio.gather(..., return_exceptions=True)` is a second net at the orchestration layer. No single provider outage can abort a run, and both output formats show every failure explicitly — a report missing its speed section must never look identical to a report where speed was fine.
**Never require elevation.** Capability detection is attempt-based, never OS-assumption-based: try unprivileged datagram ICMP, then raw ICMP, then (on Windows) the `IcmpSendEcho2` API, then fall back to TCP-connect timing and the Cloudflare `Server-Timing: cfL4` header. Worst case, the tool still produces a full report and states the degradation once, plainly, with the one-line remedy.
**Windows traceroute uses the Win32 ICMP API, not `tracert.exe` text.** `IcmpSendEcho2` from `Iphlpapi.dll` is the same unprivileged API `tracert.exe` uses internally. Driving it through `ctypes` gives per-hop RTT, TTL-expiry detection and real timeout handling with no Administrator, no Npcap and no third-party dependency — strictly better than parsing a localized three-probe text table.
**Text parsing is a last resort, and parses by line shape.** When the system binary is the only option, `traceparse.py` matches on structure (leading hop integer, RTT tokens, IP regex) and never on header or footer wording, because Windows `tracert` output is code-page dependent — cp866 on Russian Windows, not UTF-8. BSD/macOS additionally emits multiple IPs per hop and `!H`/`!N`/`!X` annotations that Linux does not, so the three parsers are genuinely separate.
**Latency never parses `ping` text.** That would mean three more locale-dependent parsers for no benefit. Every `PingResult` is tagged with the method used, because TCP "loss" (connection failure rate) and ICMP loss are different metrics and must never be silently conflated.
**Reputation defaults to sources that do not learn what you are checking.** FireHOL netsets are downloaded once, cached, and matched locally — no runtime query, no rate limit, no disclosure. Shodan InternetDB tells a home user something actionable (an exposed router, a forgotten port-forward) far more often than a mail blocklist does. Classic DNSBLs are opt-in behind `--dnsbl`.
**DNSBL responses in `127.255.255.0/24` are errors, not listings.** Spamhaus returns `127.255.255.254` for "you queried through a public resolver" — which is the case for anyone on 1.1.1.1 or 8.8.8.8 — and `127.255.255.255` for "rate limited". A naive "any `127.x.x.x` means listed" implementation red-flags most users. Both are surfaced as *result unavailable*, never as a finding.
**Bulk RIPEstat endpoints are always bounded.** `routing-history` and friends return 10 MB+ payloads for large ISPs, so every such call carries `max_rows` and a timeframe from `config.yaml`.
**PeeringDB is cached to disk.** Anonymous limits are 20 requests/minute with large responses throttled to one per hour, so a response is never re-fetched within a run and persists in `.cache/` between runs for `providers.peeringdb_cache_hours`.
**The JSON dump carries a `raw` section.** Every provider response is stored verbatim under its source key. The typed layer above it necessarily drops fields; `raw` is what actually satisfies "100% of collected data, no truncation".
## Storage
- `./logs/report_<ASN>_<YYYYMMDDTHHMMSSZ>.{md,json}` — one pair per run. Compact ISO timestamps because Windows forbids `:` in filenames. Falls back to `report_unknown_…` when the ASN lookup fails entirely. Written temp-file-plus-`os.replace()`, always atomic. Gitignored: reports contain the external IP, city, coordinates and ISP name.
- `./.cache/firehol/*.netset` — downloaded blocklists, refreshed per `providers.firehol_refresh_hours`.
- `./.cache/peeringdb/*.json` — PeeringDB responses, valid for `providers.peeringdb_cache_hours`.
- `./config.yaml` — all non-secret settings. `./.env` — the three optional API keys and nothing else.
## Configuration
`config.py` builds a pydantic-settings `Settings` object whose sources are ordered **init > environment > `.env` > `config.yaml` > field default**. The three API keys are `SecretStr | None = None` at the top level: a diagnostics tool must run with zero configuration, so a missing key downgrades the enrichment that needs it to `skipped` with a warning and never fails the run.
## Tests
`uv run pytest -q`. Roughly 120 tests covering the parsers, normalizers, verdict engine, DNSBL decoding, throughput math, ping statistics, serialization and the report diff. Glue is deliberately untested: live HTTP, real ICMP, Typer wiring, `psutil`/`ctypes` calls and the `--watch` timing loop. Traceroute fixtures live in `tests/fixtures/traceroute/{windows,linux,darwin}/` and include a cp866-encoded Russian Windows sample, because that encoding is exactly what breaks naive parsers.

91
ARCHITECTURE.ru.md Normal file
View file

@ -0,0 +1,91 @@
# Архитектура
[English](ARCHITECTURE.md)
netcheck собирает сетевые факты из множества независимых, в основном безключевых источников, сводит их в типизированные структуры, интерпретирует чистыми функциями и на каждый прогон отдаёт два артефакта. Сбор, интерпретация и рендер никогда не смешиваются: модуль либо собирает данные, либо рассуждает об уже собранных — но не одновременно.
## Структура каталогов
```
src/netcheck/
cli.py Typer-приложение: флаги, порядок фаз, прогресс Rich. Без бизнес-логики.
config.py Загрузчик pydantic-settings. Приоритет: env > .env > config.yaml > default.
models.py Все общие датаклассы плюс to_jsonable() для сериализации.
orchestration.py run_module(): тайминг, таймаут, классификация исключений в ProbeError.
netinfo.py Определение возможностей и локальные факты: интерфейс, шлюз, MTU, резолверы.
ip_geo.py Цепочка из шести провайдеров, нормализаторы, пополевое слияние.
bgp.py RIPEstat, CAIDA ASRank, Team Cymru через DNS, PeeringDB (кэш на диске).
reputation.py Кэш netset'ов FireHOL и локальный поиск, Shodan InternetDB, декодер DNSBL.
stats.py Чистая статистика RTT: потери, min/avg/max, mdev, джиттер.
traceparse.py Чистые парсеры текста в TraceHop для Windows, Linux и BSD/macOS.
interpret.py Чистый движок вердиктов: пороги -> Finding[], скоринг VPN, оценка bufferbloat.
speed.py Каскадный замер скорости, математика пропускной способности, разбор cfL4, bufferbloat.
exporter.py Рендер JSON и Markdown, атомарная запись в ./logs/.
compare.py --compare: сравнение двух сохранённых JSON-отчётов.
watch.py --watch: цикл периодических прогонов с живой панелью Rich.
probes/
latency.py Веер пингов и статистика джиттера/потерь/mdev.
traceroute.py Каскад: mtr --json -> icmp_win -> icmplib -> системный бинарник.
icmp_win.py Движок ctypes IcmpSendEcho2 / Icmp6SendEcho2 (только Windows).
dns_leak.py Перечисление резолверов по адаптерам, echo-пробы, детект ECS.
```
## Поток данных
```
+-- netinfo (локальные факты, возможности)
|
cli.py -------+-- цепочка ip_geo ---> внешний IP + ASN [блокирующая фаза: всё дальше зависит от ASN]
|
+-- bgp || reputation || dns_leak (параллельно)
|
+-- latency || веер traceroute (параллельно, ограничено семафором)
|
+-- speed (эксклюзивная фаза)
|
+-- interpret ---> Finding[] + общий вердикт
|
+-- exporter ---> logs/report_<ASN>_<ts>.md + .json
```
Два жёстких ограничения по конкурентности:
1. **Изоляция измерений.** Замер скорости никогда не пересекается с трассировкой, пингами и запросами к API — он держит эксклюзивную блокировку фазы. Единственное намеренное исключение — проба bufferbloat, которая по замыслу работает во время насыщения канала, внутри `speed.py`.
2. **Ограниченный веер трассировок.** Параллельные трассировки к разным целям делят первые хопы и завышают задержки друг друга, поэтому число одновременных трассировок ограничено семафором (`probing.trace_concurrency`, по умолчанию 2).
## Ключевые решения
**Отказ — это значение, а не поток управления.** Каждый вызов модуля проходит через `run_module()`: он замеряет время, применяет таймаут на модуль, перехватывает всё кроме `CancelledError` и возвращает `ModuleResult` со статусом `ok | partial | failed | skipped`. `asyncio.gather(..., return_exceptions=True)` — вторая страховка на уровне оркестрации. Ни один сбой провайдера не может прервать прогон, и оба формата вывода показывают каждый сбой явно: отчёт без раздела скорости не должен выглядеть так же, как отчёт, где со скоростью всё было хорошо.
**Никогда не требовать повышения прав.** Определение возможностей построено на попытках, а не на предположениях об ОС: сначала непривилегированный datagram ICMP, затем raw ICMP, затем (на Windows) API `IcmpSendEcho2`, затем откат на тайминг TCP-connect и заголовок Cloudflare `Server-Timing: cfL4`. В худшем случае инструмент всё равно выдаёт полный отчёт и один раз, простыми словами, сообщает о деградации и способе её устранить.
**Трассировка на Windows использует Win32 ICMP API, а не текст `tracert.exe`.** `IcmpSendEcho2` из `Iphlpapi.dll` — тот же непривилегированный API, который `tracert.exe` использует внутри. Через `ctypes` он даёт RTT по хопам, определение истёкшего TTL и корректные таймауты без администратора, Npcap и сторонних зависимостей — строго лучше, чем разбор локализованной таблицы из трёх проб.
**Разбор текста — крайняя мера, и он опирается на форму строки.** Когда системный бинарник — единственный вариант, `traceparse.py` смотрит на структуру (ведущее целое хопа, токены RTT, регулярка IP) и никогда на слова в заголовке или подвале: вывод `tracert` зависит от кодовой страницы — cp866 на русской Windows, а не UTF-8. BSD/macOS вдобавок выдаёт несколько IP на хоп и аннотации `!H`/`!N`/`!X`, которых нет в Linux, поэтому три парсера действительно разные.
**Замер задержек не разбирает текст `ping`.** Это означал бы ещё три locale-зависимых парсера без всякой пользы. Каждый `PingResult` помечен использованным методом, потому что «потери» TCP (доля неудачных подключений) и потери ICMP — разные метрики, и молча смешивать их в отчёте нельзя.
**Репутация по умолчанию опирается на источники, которые не узнают, что вы проверяете.** Netset'ы FireHOL скачиваются один раз, кэшируются и сопоставляются локально — ни запроса наружу, ни лимитов, ни раскрытия. Shodan InternetDB для домашнего пользователя гораздо чаще говорит что-то полезное (открытый роутер, забытый проброс порта), чем почтовый блоклист. Классические DNSBL включаются явно флагом `--dnsbl`.
**Ответы DNSBL в диапазоне `127.255.255.0/24` — это ошибки, а не листинг.** Spamhaus возвращает `127.255.255.254` на «вы спросили через публичный резолвер» — а это все, кто сидит на 1.1.1.1 или 8.8.8.8 — и `127.255.255.255` на «превышен лимит». Наивная реализация «любой `127.x.x.x` значит в списке» помечает красным большинство пользователей. Оба случая подаются как *результат недоступен*, но никогда как находка.
**Массовые эндпоинты RIPEstat всегда ограничены.** `routing-history` и подобные на крупных провайдерах возвращают 10 МБ+, поэтому каждый такой вызов несёт `max_rows` и временное окно из `config.yaml`.
**PeeringDB кэшируется на диск.** Анонимные лимиты — 20 запросов в минуту, а крупные ответы душатся до одного в час, поэтому ответ не запрашивается повторно внутри прогона и сохраняется в `.cache/` между прогонами на `providers.peeringdb_cache_hours`.
**В JSON-дампе есть раздел `raw`.** Каждый ответ провайдера сохраняется дословно под ключом источника. Типизированный слой над ним неизбежно теряет поля; именно `raw` действительно выполняет требование «100% собранных данных без усечения».
## Хранение
- `./logs/report_<ASN>_<YYYYMMDDTHHMMSSZ>.{md,json}` — пара файлов на прогон. Компактный ISO-таймстамп, потому что Windows запрещает `:` в именах файлов. При полном провале определения ASN — `report_unknown_…`. Запись через временный файл и `os.replace()`, всегда атомарно. В gitignore: отчёты содержат внешний IP, город, координаты и название провайдера.
- `./.cache/firehol/*.netset` — скачанные блоклисты, обновляются по `providers.firehol_refresh_hours`.
- `./.cache/peeringdb/*.json` — ответы PeeringDB, действительны `providers.peeringdb_cache_hours`.
- `./config.yaml` — все несекретные настройки. `./.env` — три опциональных API-ключа и ничего больше.
## Конфигурация
`config.py` собирает объект `Settings` на pydantic-settings, источники которого упорядочены **init > переменные окружения > `.env` > `config.yaml` > значение по умолчанию**. Три API-ключа объявлены как `SecretStr | None = None` на верхнем уровне: диагностический инструмент обязан работать с нулевой настройкой, поэтому отсутствие ключа переводит зависящее от него обогащение в `skipped` с предупреждением и никогда не роняет прогон.
## Тесты
`uv run pytest -q`. Около 120 тестов: парсеры, нормализаторы, движок вердиктов, декодирование DNSBL, математика пропускной способности, статистика пингов, сериализация и сравнение отчётов. Клей намеренно не тестируется: живой HTTP, настоящий ICMP, обвязка Typer, вызовы `psutil`/`ctypes` и цикл `--watch`. Фикстуры трассировок лежат в `tests/fixtures/traceroute/{windows,linux,darwin}/` и включают образец русской Windows в cp866 — именно эта кодировка ломает наивные парсеры.

View file

@ -1,5 +1,96 @@
# netcheck
Cross-platform CLI for deep network diagnostics: ISP/VPN/ASN identity, latency, path and bandwidth.
[Русский](README.ru.md)
(Full documentation to be written.)
Cross-platform CLI for deep network diagnostics: who your ISP actually is, whether a VPN is really carrying your traffic, where the path degrades, and what your line actually delivers.
```console
$ netcheck --quick
netcheck 0.1.0 · auto mode · Windows
Connection Wi-Fi 2 · 192.168.1.34 · MTU 1500 · gw 192.168.1.1
Egress 203.0.113.44 (AS64500 Example Telecom, Amsterdam NL)
VPN none confidence 0.10
Latency 1.1.1.1 12.4 ms avg · jitter 1.9 ms · loss 0%
Verdict 🟢 healthy — no problems found
Report written to logs/report_AS64500_20260808T191200Z.md
logs/report_AS64500_20260808T191200Z.json
```
## Install
Requirements:
- Python 3.10 or newer
- Windows, Linux or macOS
- Internet access (netcheck is a network tool; it degrades gracefully but cannot do much offline)
- Optional: the native Ookla `speedtest` binary on `PATH` for the best bandwidth tier
- Optional: `mtr` on `PATH` for the best path data
```bash
git clone https://github.com/<owner>/netcheck
cd netcheck
uv sync
uv run netcheck
```
Without `uv`:
```bash
python -m pip install -r requirements.txt
python -m pip install -e .
netcheck
```
No configuration is required. Copy `.env.example` to `.env` only if you have optional API keys.
## What it does
- **Identity** — egress IPv4 *and* IPv6, reverse DNS, ASN, organisation, geo, and whether the address looks residential, mobile, hosting or business. Cross-checked across six independent providers so one rate limit does not blank the answer.
- **VPN / proxy assessment** — a weighted verdict (`none` / `likely` / `confirmed`) built from tunnel interfaces, MTU anomalies, gateway-vs-egress mismatch, Cloudflare WARP flags, provider proxy/hosting flags, PeeringDB network type, timezone-vs-geo mismatch, and DNS resolver ASN mismatch.
- **DNS leak test** — enumerates resolvers *per network adapter*, so it catches the classic case where the tunnel adapter is clean but the Wi-Fi adapter still holds the ISP resolver. Also detects EDNS Client Subnet leakage.
- **BGP intelligence** — upstreams, peers, downstreams, announced prefixes, route stability, IXP presence, and CAIDA ASRank customer-cone size.
- **Reputation** — locally cached FireHOL blocklists (no query leaves your machine), Shodan InternetDB exposure, and optional classic DNSBL zones.
- **Latency & path** — per-host ping statistics with real jitter/loss/mdev, and a traceroute that never needs administrator rights.
- **Bandwidth** — a cascading speedtest (Ookla binary → Cloudflare → fast.com → optional M-Lab NDT7) with separate download and upload bufferbloat grades.
## Handy things
| Flag | What it does |
|---|---|
| `--quick` | ~10 s express run: reference-host pings only, no speedtest |
| `--full` | ~60–120 s: full speedtest and full MTR cycles |
| `--target AS64500` / `--target 1.2.3.4` / `--target example.com` | Investigate a specific network instead of your own connection |
| `--target-host <ip\|domain>` | Add an extra host to the ping and traceroute fan-out |
| `--speedtest-server <id\|url>` | Pin the speedtest to a specific server |
| `--watch` | Continuous monitoring with a live dashboard — for catching intermittent drops |
| `--compare a.json b.json` | Diff two earlier reports: before/after a VPN switch, before/after an ISP call |
| `--dnsbl` | Opt in to classic DNSBL reputation zones |
| `--ndt7` | Opt in to M-Lab NDT7 (publishes your measurement, including your IP, as CC0 open data) |
| `--tcp-trace` | Opt in to a scapy TCP-SYN traceroute through ICMP-filtering middleboxes (needs Npcap or root) |
Every run writes two files into `./logs/`: a Markdown report to read and a JSON dump containing every raw provider response, untruncated. `./logs/` is gitignored — reports contain your external IP, city, coordinates and ISP name.
netcheck never requires administrator or root. Where privileges would give better data, it degrades to an unprivileged method and says so in the report.
## For developers
Stack: Python 3.10+, Typer, Rich, httpx, pydantic-settings, dnspython, psutil, icmplib, ctypes.
```bash
uv sync --all-extras --group dev
uv run pytest -q
uv run netcheck --quick
uv export --no-hashes --format requirements-txt -o requirements.txt # after dependency changes
```
Architecture, data flow and the reasoning behind each provider choice: [ARCHITECTURE.md](ARCHITECTURE.md).
## Credits
netcheck reads from RIPEstat, CAIDA ASRank, Team Cymru, PeeringDB, Cloudflare, ip-api.com, freeipapi.com, ipinfo.io, ipwho.is, Shodan InternetDB, the FireHOL blocklist project, Netflix fast.com and M-Lab. All of them are used within their free/keyless tiers.
## License
Not yet finalized — see [LICENSE](LICENSE). The choice is between MIT and Polyform Noncommercial; the default reputation sources (Shodan InternetDB, Spamhaus free mirrors) are non-commercial-use-only, which is the crux of the decision.

96
README.ru.md Normal file
View file

@ -0,0 +1,96 @@
# netcheck
[English](README.md)
Кроссплатформенная CLI-утилита для глубокой диагностики сети: кто на самом деле ваш провайдер, действительно ли трафик идёт через VPN, где деградирует маршрут и что реально выдаёт канал.
```console
$ netcheck --quick
netcheck 0.1.0 · авто-режим · Windows
Соединение Wi-Fi 2 · 192.168.1.34 · MTU 1500 · шлюз 192.168.1.1
Внешний IP 203.0.113.44 (AS64500 Example Telecom, Амстердам NL)
VPN нет уверенность 0.10
Задержка 1.1.1.1 12.4 мс сред. · джиттер 1.9 мс · потери 0%
Вердикт 🟢 всё в порядке — проблем не найдено
Отчёт записан в logs/report_AS64500_20260808T191200Z.md
logs/report_AS64500_20260808T191200Z.json
```
## Установка
Требования:
- Python 3.10 или новее
- Windows, Linux или macOS
- Доступ в интернет (netcheck — сетевой инструмент; без сети он деградирует корректно, но сделает немного)
- Опционально: нативный бинарник Ookla `speedtest` в `PATH` — лучший уровень замера скорости
- Опционально: `mtr` в `PATH` — лучшие данные по маршруту
```bash
git clone https://github.com/<owner>/netcheck
cd netcheck
uv sync
uv run netcheck
```
Без `uv`:
```bash
python -m pip install -r requirements.txt
python -m pip install -e .
netcheck
```
Настройка не требуется. Копируйте `.env.example` в `.env` только если у вас есть опциональные API-ключи.
## Что он делает
- **Идентификация** — внешний IPv4 *и* IPv6, обратный DNS, ASN, организация, гео и тип адреса: домашний, мобильный, хостинговый или корпоративный. Проверяется по шести независимым источникам, поэтому один rate-limit не обнуляет результат.
- **Оценка VPN/прокси** — взвешенный вердикт (`none` / `likely` / `confirmed`) из наличия туннельного интерфейса, аномалии MTU, расхождения шлюза и внешнего IP, флагов Cloudflare WARP, флагов proxy/hosting от провайдеров данных, типа сети в PeeringDB, расхождения таймзоны ОС и гео, а также несовпадения ASN DNS-резолвера.
- **Тест утечки DNS** — перечисляет резолверы *по каждому сетевому адаптеру*, поэтому ловит классический случай, когда туннельный адаптер чистый, а на Wi-Fi-адаптере остался резолвер провайдера. Также определяет утечку EDNS Client Subnet.
- **BGP-разведка** — аплинки, пиры, клиенты, анонсируемые префиксы, стабильность маршрутов, присутствие на IX и размер клиентского конуса по CAIDA ASRank.
- **Репутация** — локально кэшируемые списки FireHOL (ни один запрос не покидает машину), экспозиция по Shodan InternetDB и опционально классические зоны DNSBL.
- **Задержки и маршрут** — статистика пингов по каждому хосту с настоящими джиттером/потерями/mdev и трассировка, которой никогда не нужны права администратора.
- **Скорость** — каскадный замер (бинарник Ookla → Cloudflare → fast.com → опционально M-Lab NDT7) с раздельными оценками bufferbloat на приём и передачу.
## Полезное
| Флаг | Что делает |
|---|---|
| `--quick` | Экспресс-прогон ~10 с: только пинги эталонных хостов, без замера скорости |
| `--full` | 60–120 с: полный замер скорости и полный MTR |
| `--target AS64500` / `--target 1.2.3.4` / `--target example.com` | Исследовать конкретную сеть вместо своего подключения |
| `--target-host <ip\|domain>` | Добавить хост в веер пингов и трассировок |
| `--speedtest-server <id\|url>` | Зафиксировать конкретный сервер замера скорости |
| `--watch` | Непрерывный мониторинг с живой панелью — чтобы поймать плавающие обрывы |
| `--compare a.json b.json` | Сравнить два прошлых отчёта: до/после смены VPN, до/после звонка провайдеру |
| `--dnsbl` | Включить проверку по классическим зонам DNSBL |
| `--ndt7` | Включить M-Lab NDT7 (замер публикуется как открытые данные CC0, включая ваш IP) |
| `--tcp-trace` | Включить TCP-SYN-трассировку через scapy сквозь middlebox'ы, режущие ICMP (нужен Npcap или root) |
Каждый прогон пишет в `./logs/` два файла: Markdown-отчёт для чтения и JSON-дамп со всеми сырыми ответами провайдеров без усечения. `./logs/` в gitignore — отчёты содержат ваш внешний IP, город, координаты и название провайдера.
netcheck никогда не требует администратора или root. Там, где привилегии дали бы данные лучше, он деградирует до непривилегированного метода и пишет об этом в отчёте.
## Для разработчиков
Стек: Python 3.10+, Typer, Rich, httpx, pydantic-settings, dnspython, psutil, icmplib, ctypes.
```bash
uv sync --all-extras --group dev
uv run pytest -q
uv run netcheck --quick
uv export --no-hashes --format requirements-txt -o requirements.txt # после изменения зависимостей
```
Архитектура, потоки данных и обоснование выбора каждого источника: [ARCHITECTURE.ru.md](ARCHITECTURE.ru.md).
## Благодарности
netcheck читает данные из RIPEstat, CAIDA ASRank, Team Cymru, PeeringDB, Cloudflare, ip-api.com, freeipapi.com, ipinfo.io, ipwho.is, Shodan InternetDB, проекта блоклистов FireHOL, Netflix fast.com и M-Lab. Все они используются в рамках бесплатных/безключевых тарифов.
## Лицензия
Пока не выбрана — см. [LICENSE](LICENSE). Выбор между MIT и Polyform Noncommercial; источники репутации по умолчанию (Shodan InternetDB, свободные зеркала Spamhaus) разрешены только для некоммерческого использования — в этом суть решения.