TraceHop.asn/as_name existed in the model since the start but nothing ever
populated them. New probes/hop_asn.py fires one Team Cymru DNS query pair
per unique hop IP (in-run deduped, no rate limit unlike the cached_json()
providers) after the latency/path/TLS fan-out, plus a PTR lookup for hops
whose reverse_dns is still empty because the backend that produced them
doesn't resolve hostnames itself (icmp_win/icmplib/mtr, unlike system
traceroute text).
New path.detour_country finding flags a route that spends two or more
consecutive hops in a country that's neither the client's nor the
destination's. Wires up providers.cymru_asn_zone, which was configured but
referenced nowhere, and adds cymru_origin6_zone for the IPv6 branch Phase 8
will need.
Part of plans/zesty-zooming-wilkinson.md (gitignored).
- net.cgnat: three evidence paths (local IP, gateway, traceroute hop) for
RFC 6598 100.64.0.0/10, since the local address alone misses the common
home-router case
- net.nat64: RFC 7050 ipv4only.arpa AAAA probe (new probes/nat64.py) instead
of checking the client's own address against 64:ff9b::/96, which never
fires since that prefix is a synthesis target, not something the client's
egress address falls into
- path.first_hop_loss/slow/unexpected: path_findings() takes an optional
LocalNet/Thresholds so loss or latency at hop 1 is attributed to the
user's own router/Wi-Fi instead of blending into the ISP-facing mid-path
logic
All four findings are always-on additions to existing report sections, no
new CLI flags. Part of the larger extension batch tracked in
plans/zesty-zooming-wilkinson.md (gitignored, local planning doc).
Every run used to write all three files (en.md, ru.md, json) unconditionally.
Default is now English Markdown only, sourced from output.formats in
config.yaml; --format/--ru/--json pick exactly what a run writes, and any
explicit --format replaces the default rather than adding to it (all/none
supported). write_report() moved from a fixed 3-positional-arg signature to
a {format: text} mapping so writes are conditional instead of always-three.
The en/ru Markdown cross-link header is now only set when both languages are
actually being written.