This release hardens SwiftFTR's network protocols and async lifecycle, preserves the exact endpoint
used by traceroute, adds IPv4/IPv6 UDP route binding, and removes numbered BSD-name guesses for
physical Wi-Fi/Ethernet roles. See
Migrating to SwiftFTR 0.14 for adopter-focused
examples and a checklist.
Public API and compatibility
TraceResult.resolvedIPreports the exact numeric address used for probing. Classified trace and
multipath enrichment reuse that address instead of resolving the hostname a second time.
#29UDPProbeConfigandudpProbe(...)accept an exactinterfaceand family-matchedsourceIP.
EmptyDatanow sends a true zero-byte datagram rather than one NUL byte.
#34DNSErroraddsinvalidTimeoutandsetsockoptFailed. Exhaustive downstream switches must add
both known cases. An ordinarydefaulthandles grouped errors;@unknown defaultdoes not stand
in for currently known cases.
#43- Compatibility overloads retain the callable 0.13 initializer/function shapes for
TraceResult,
UDPProbeConfig, andudpProbe. The API compatibility diagnostic against v0.13.0 therefore
reports only the two intentionalDNSErroradditions.
Behavior changes
- Invalid numeric, range, duration, and payload-size values for trace, streaming trace, ping,
TCP/UDP, bufferbloat, and multipath operations are validated before network work. Nonthrowing
configuration initializers retain values instead of trapping; throwing operation paths report
TracerouteError.invalidConfiguration. Host resolution and route binding retain their existing
API-specific error channels.
#32
#33 httpProbecompletes when response headers arrive and cancels the body transfer.rtttherefore
measures through header receipt rather than full-body download. URLSession'snetworkRTTand
tcpHandshakeRTTremain best-effort and may benil. Only absolute HTTP/HTTPS URLs with a host
and finite positive timeouts are accepted.
#37swift-ftr probe tcpexits with a failure status when its structured result reports an
unreachable target or route-binding error. Successful probes, including reachable closed ports,
continue to exit successfully.- Loaded bufferbloat tests reject effective interface or source-address binding because URLSession
load traffic cannot be guaranteed to follow the bound ping route. Bound baseline-only runs remain
available withloadDuration: 0.
#38 - Multipath remains IPv4-only and rejects forced IPv6 or IPv6 source configurations before
launching workers. Empty decoded topologies no longer trap during divergence analysis.
#29
#40 reverseDNSreturnsnilwhen no PTR hostname exists instead of returning the numeric address.
#35- Classification handles IPv6 unique-local, link-local, loopback, unspecified and multicast
scopes, IPv4-mapped IPv6, CGNAT, and VPN-local addresses consistently. Non-global addresses are
not sent to ASN resolvers, and exact destination identity takes precedence over ASN heuristics.
#36 - Physical interface type comes from macOS SystemConfiguration metadata. Unknown names are
.other;
callers must discover interfaces dynamically and must not infer Wi-Fi or Ethernet from a BSD-name
prefix or numeric suffix.
#48
Cancellation, concurrency, and caching
- Ending a streaming trace or reacting to
networkChanged()cancels its active socket, timers,
producer, and retry work.
#27 - Ping lifecycle cleanup is serialized; setup failures close sockets, sender tasks are joined, and
cancellation is idempotent.
#30 - TCP and UDP probes propagate task cancellation promptly instead of remaining alive until timeout.
#34
#42 - Bufferbloat and multipath work remains in the caller's structured task tree. Sibling work is
canceled and joined on failure or cancellation.
#38
#39 - Selected legacy DNS, STUN/public-IP enrichment, ASN, and cached-rDNS blocking calls run on a
Dispatch-backed queue capped at eight operations instead of Swift's cooperative executor. Once
submitted, cancellation waits for a synchronous operation to finish; configured socket timeouts
bound the calls that have them.
#41 - Public-IP and rDNS cache generations prevent work started on an old network from repopulating
cleared caches. Concurrent ASN misses are coalesced by resolver identity, address, and timeout.
#31
#51
Protocol and parser hardening
- STUN sockets connect to the selected server, and responses must match the Binding Success type,
declared length, magic cookie, transaction ID, and requested address family.
#25 - DNS UDP responses must come from the selected resolver and match the transaction ID, response bit,
opcode, truncation state, and response code. TXT queries use the same validated path. Truncated
UDP replies are rejected; DNS-over-TCP fallback is not yet implemented.
#28 - DNS validates timeouts and QNAME/PTR inputs before socket conversion. Compressed names and
name-bearing RDATA are bounded to their records and reject invalid pointers, cycles, overlong
expansion, and cross-record borrowing.
#43
#45 - ICMP error correlation validates the quoted IP protocol, header shape, Echo Request type/code,
and IPv6 Next Header before matching identifiers and sequences.
#44
CLI changes
swift-ftr --versionreports the same release version embedded in trace JSON output.swift-ftr pinguses-ifor interval and-Ifor interface; long options are unchanged.
#46- Trace JSON honors
--no-rdnsand avoids all reverse lookups when it is present.
#47 swift-ftr interfacesshows active interfaces by default;--include-inactiveincludes down
interfaces.
#49
Routing contracts
- Interface names represent the caller's exact OS-discovered selection. Binding applies only to an
API's documented probe sockets. Hostname resolution, system rDNS, DNS-whoami fallback, Team Cymru
ASN queries, and URLSession HTTP traffic remain system-routed. getPublicIPs()is uncached, dual-stack, and STUN-only.SwiftFTRConfig.publicIPapplies to
classified trace and multipath enrichment, not standalone public-IP calls.- Public HTTP/HTTPS probes do not expose production interface or source-address binding.
Dependencies
- The SwiftIP2ASN floor is now 0.4.1, adding self-healing disk cache behavior, cache-poisoning
prevention, robust missing-file refresh, and the July 2026 dual-stack database.
Development and release tooling
- CI separates deterministic offline checks from opt-in live-network tests and validates
documentation commands.
#26 - UDP loopback tests have bounded waits and deterministic cancellation cleanup; production behavior
is unchanged.
#52 - Address-copy helpers explicitly discard
memcpyreturn values so Swift 6.3 builds remain
warning-free. - The security policy lists the 0.14.x release line as supported.
- The release workflow uses current Node 24 action generations, requires an existing annotated
version tag with a matching CLI version, and emits one explicitly named CycloneDX SBOM instead of
allowing the SBOM action to upload a duplicate asset. - An internal
NWConnectionHTTPS spike validates exact-interface IPv4/IPv6 TLS, SNI, default
trust, Host, User-Agent, bounded headers, and cancellation. It is dormant, not wired into
httpProbe, and is not production API. The production roadmap retains URLSession for
unconstrained probes and forbids silent route fallback.
#53
Full Changelog: v0.13.0...v0.14.0