package netreport

Import Path
	github.com/tmc/go-iroh/internal/netreport (on go.dev)

Dependency Relation
	imports 15 packages, and imported by one package

Involved Source Files client.go defaults.go Package netreport probes the local network environment to build a [Report] describing relay latencies and (where available) the public reflexive address of the host. It is a port of iroh's net_report module (iroh/src/net_report). A [Client] runs three kinds of probes against the configured relays: - HTTPS probes fetch the relay's "/ping" path and time the round trip. - QAD probes open a QUIC Address Discovery connection (ALPN "/iroh-qad/0", QUIC port 7842) and read the connection RTT. - A captive-portal check fetches "/generate_204" with an X-Iroh-Challenge header and verifies the X-Iroh-Response echo, but only on full reports. From those probes the client picks a preferred relay, applying hysteresis so a responsive relay is not abandoned unless a new one is meaningfully faster. # Reflexive address discovery A QAD connection also learns the host's public address from the relay via the QUIC Address Discovery observed-address extension (draft-seemann-quic-address-discovery), implemented in the forked quic-go [github.com/tmc/go-iroh/internal/qng] (slice X3). The QAD client advertises the receive-only address-discovery role, and a QAD relay reports the client's reflexive address with OBSERVED_ADDRESS frames; the latest report is surfaced as [Report.GlobalV4]/[Report.GlobalV6] (and the matching UDP fields). Reports arrive after the handshake, so [qadConn.observedAddr] waits briefly for the first one; an immediate read would always miss. Without a report (not negotiated, or timed out) the probe stays latency-only. Which socket a probe rides decides what the observed address means: a private per-probe socket learns a mapping that dies with it. [Client.WithQADDialer] routes probes through the caller's transport instead, so the observed address is the caller's real public mapping. probe.go probes.go qad.go report.go
Code Examples package main import ( "context" "fmt" "github.com/tmc/go-iroh/internal/netreport" "github.com/tmc/go-iroh/relay" ) func main() { client := netreport.NewClient(relay.NewMap()) report, err := client.GetReport(context.Background(), netreport.IfStateDetails{HaveV4: true}, true) if err != nil { fmt.Println("error:", err) return } fmt.Println("has udp:", report.HasUDP()) fmt.Println("preferred relay set:", !report.PreferredRelay.IsZero()) } package main import ( "fmt" "github.com/tmc/go-iroh/internal/netreport" ) func main() { fmt.Println(netreport.ProbeHTTPS) fmt.Println(netreport.ProbeQADv4) fmt.Println(netreport.ProbeQADv6) }
Package-Level Type Names (total 6)
/* sort by: | */
Client runs net_report probes against a set of relays and tracks report history so it can apply preferred-relay hysteresis across runs. It is a port of net_report::Client (iroh/src/net_report.rs:90). A Client is safe for concurrent use; GetReport serializes internally. The zero value is not usable; construct a Client with [NewClient]. GetReport runs a single net_report. doFull forces a full report (captive portal check and reset probe history); otherwise the report is full only if no report has been generated within [fullReportInterval]. ifState informs the sufficiency check. The whole call is bounded by [overallReportTimeout]; the probes within it are bounded by [probesTimeout]. If the relay map is empty the report is empty: no probes run and PreferredRelay is the zero value. WithDNSResolver sets the resolver used to look up relay hostnames. WithQADDialer routes QAD probes through d instead of a private per-probe UDP socket, so the observed address is the mapping of the dialer's own socket. A per-probe socket's mapping dies with it and its port is nobody's dial candidate; per-probe mappings also differ between relays, which makes MappingVariesByDest misreport symmetric NAT. WithQADTLSConfig sets the TLS verification policy for QAD QUIC connections. It is used in tests to trust a self-signed relay certificate. WithQUICConfig sets the QAD QUIC transport configuration. WithTLSConfig sets the TLS configuration used for HTTPS probes. It is used in tests to trust self-signed relay certificates. func NewClient(relayMap *relay.Map) *Client func (*Client).WithDNSResolver(r *net.Resolver) *Client func (*Client).WithQADDialer(d QADDialer) *Client func (*Client).WithQADTLSConfig(cfg *itls.Config) *Client func (*Client).WithQUICConfig(cfg *quic.Config) *Client func (*Client).WithTLSConfig(cfg *tls.Config) *Client
IfStateDetails describes the host's interface capabilities, used to decide when enough probes have completed. It is a port of net_report's IfStateDetails (iroh/src/net_report/reportgen.rs:74). HaveV4 reports whether the host has IPv4 connectivity. HaveV6 reports whether the host has IPv6 connectivity. func (*Client).GetReport(ctx context.Context, ifState IfStateDetails, doFull bool) (*Report, error)
Probe identifies how a relay's latency was measured. The order matches the Rust enum (iroh/src/net_report/probes.rs:22) so a [Probe] sorts and prints consistently across the port. String returns the probe name, matching the Rust Display impl. Probe : expvar.Var Probe : fmt.Stringer const ProbeHTTPS const ProbeQADv4 const ProbeQADv6
QADDialer opens the QUIC connection for one QAD probe to addr; tlsConf already carries the QAD ALPN and server name. func (*Client).WithQADDialer(d QADDialer) *Client
RelayLatencies records the lowest latency seen per relay for each probe type. It is a port of net_report::RelayLatencies (report.rs:130). The zero value is empty and ready to use. Snapshot returns the lowest latency recorded for each relay.
Report describes the network environment as measured by a single [Client.GetReport] run. It is a port of iroh's net_report::Report (iroh/src/net_report/report.rs:14). The zero Report is a valid empty report. CaptivePortal reports whether a captive portal is intercepting HTTP, when the check ran (full reports only). Full reports whether this run used the full probe plan. GlobalV4 is the host's public IPv4 address as seen by a relay. It is absent when no QAD IPv4 observed-address report arrived. GlobalV6 is the host's public IPv6 address as seen by a relay. It is absent when no QAD IPv6 observed-address report arrived. MappingVariesByDestV4 reports whether the observed public IPv4 address differs across relays, when known. MappingVariesByDestV6 reports whether the observed public IPv6 address differs across relays, when known. PreferredRelay is the relay with the best recent latency, chosen with hysteresis. It is the zero RelayURL when no relay responded. RelayLatency holds per-relay, per-probe latencies. UDPv4 reports whether a QAD IPv4 round trip completed and reported an observed IPv4 address. UDPv6 reports whether a QAD IPv6 round trip completed and reported an observed IPv6 address. HasUDP reports whether any QAD round trip succeeded. func (*Client).GetReport(ctx context.Context, ifState IfStateDetails, doFull bool) (*Report, error)
Package-Level Functions (only one)
NewClient returns a Client that probes the relays in relayMap.
Package-Level Variables (only one)
ErrExtensionNotNegotiated is returned by [qadConn.observedAddr] when no reflexive address is available: either QUIC Address Discovery was not negotiated on the connection, or no OBSERVED_ADDRESS report arrived within [qadObservedAddrWait]. The forked quic-go implements the extension (slice X3); see the package doc.
Package-Level Constants (total 3)
ProbeHTTPS times an HTTPS GET of the relay's probe path.
ProbeQADv4 times a QUIC Address Discovery connection over IPv4.
ProbeQADv6 times a QUIC Address Discovery connection over IPv6.