Network Diagnostics Suite

Technical report / Revision 1

From browser speed test to cross-platform diagnostics.

How benchmarking against the M-Lab test surfaced through Google Search exposed a delivery bottleneck, and how the project evolved into compatible browser, desktop, and command-line interface (CLI) measurement clients.

Launch tool Source
Revision
Project baseline
b1c549c
Scope
Browser · Desktop · CLI
Report schema
2.0

Johnny Li · Revision 1 · 2026-08-05 · Network Diagnostics Suite baseline b1c549c · johnnyli.dev/projects/network-diagnostics-suite/report/

Abstract

A connection-quality system built around evidence boundaries.

Network Diagnostics Suite began as a browser test for throughput and latency under load. It became a shared measurement system after the browser’s limits, disagreements with M-Lab, and the need for operating-system evidence made a single-client design inadequate.

Report only what the active client actually measured. Browser failures remain request loss. Packet loss, routes, Wi-Fi, resolver timing, path MTU, and interface evidence require native access. Missing evidence is shown as “Not measured,” not guessed.

01

From speed test to diagnostic system

The project began with a practical question: why can a fast connection still feel bad?

Ordinary speed tests compress a connection into download and upload headlines. Those numbers matter, but they do not show whether latency rises during transfer, whether one flow behaves differently from several, whether the result is still ramping, or whether failures occur around an otherwise healthy average.

The browser application therefore measured idle latency, loaded latency during download and upload, latency distributions, jitter, request failures, single-flow and aggregate throughput, and connection-count scaling. The design kept the barrier to entry low: no installation, account, telemetry, or project-operated result database.

That browser-first scope exposed the next problem. Several questions users naturally ask—packet loss, gateway health, traceroute, DNS resolver timing, route choice, Wi-Fi condition, path MTU, and local-link capacity—cannot be answered truthfully by webpage JavaScript. The architecture expanded instead of relabeling browser approximations.

Browser

Immediate connection quality

First-party transfer, responsiveness under load, request loss, scaling, endpoint selection, and locally stored reports.

Desktop

Native diagnostic workbench

Connection Check, Quick, Full, and Stress plus operating-system, interface, route, resolver, path, and local area network (LAN) evidence.

CLI

Scriptable deep probe

Repeatable Internet Control Message Protocol (ICMP), traceroute, Domain Name System (DNS), Maximum Transmission Unit (MTU), TCP/TLS, Wi-Fi, routing, optional Internet transfer, and two-machine LAN testing.

Report

Compatible evidence envelope

Schema 2.0 carries producer identity, plan, endpoint context, measurements, findings, and optional native or LAN sections.

02

The browser boundary

A browser can measure application behavior precisely without having raw network visibility.

Idle latency uses uncached requests to the project’s ping endpoint. Successful requests produce minimum, maximum, mean, median, 95th percentile, and consecutive-sample jitter. Failed or timed-out requests divided by attempts produce browser request loss.

That value is not packet loss. Transmission Control Protocol may retransmit below the browser, while failures can originate in Domain Name System resolution, Transport Layer Security setup, HTTP handling, extensions, browser scheduling, endpoint behavior, or the path itself. The browser cannot isolate those layers.

The same boundary excludes truthful traceroute, default-gateway testing, route-table inspection, raw ICMP, path Maximum Transmission Unit discovery, source-interface socket binding, and reliable Wi-Fi radio details. Rather than invent those fields, the project added a native core and made capability status part of the report.

03

Benchmarking against the M-Lab test used by Google Search

A large download gap became evidence that the test might be measuring its own delivery path.

Early comparisons repeatedly showed M-Lab reporting much higher download speed while upload stayed close. One observed pair was approximately 381 Mbps through M-Lab versus 182 Mbps through Network Diagnostics Suite. A later comparison showed 328.7 Mbps down and 86.2 Mbps up through M-Lab, versus roughly 198–206 Mbps down and 85–91 Mbps up through the project.[2][4]

The close upload values mattered. They weakened the idea that the browser, access connection, or all Cloudflare traffic was universally capped near 200 Mbps. The discrepancy was concentrated in the project’s download implementation.

Research into Network Diagnostic Tool version 7 (NDT7) corrected another assumption: M-Lab was not simply opening many parallel browser downloads. NDT7 uses one long-lived WebSocket over one TCP connection, usually to a nearby well-provisioned server, with Bottleneck Bandwidth and Round-trip propagation time (BBR) where available and server-side TCP instrumentation. It measures application-level goodput over a mature measurement path.[1][3]

Development observations across the M-Lab and project measurement paths
Development observationM-LabNetwork Diagnostics SuiteInterpretation
Early comparison≈381 Mbps down≈182 Mbps downThe gap was too large to dismiss without isolating the serving path.
Later comparison328.7 down / 86.2 up≈198–206 down / 85–91 upUpload agreement pointed toward a download-specific bottleneck.
Post-change observationNot run concurrently≈370.1 median / 366.3 steady downThe earlier 180–200 Mbps ceiling was not an unavoidable browser limit.

Evidence boundary: these values were retained from back-to-back development testing, not a controlled benchmark dataset. The earliest observations did not preserve exact timestamps, browser build, device configuration, selected M-Lab server, or exported reports. They are included as engineering observations; the project-controlled R2-versus-Worker A/B below is the stronger causal evidence.

04

Finding the download bottleneck

The clearest experiment compared the connection through two project-controlled delivery paths.

The initial engine repeatedly requested a dynamically composed Worker stream. Even with cache hits, concurrency, and modern protocol negotiation, throughput oscillated and frequently settled near 200 Mbps. Request turnover could create brief peaks without sustaining the same rate.

The project then added a large deterministic incompressible object on a direct R2 custom domain while retaining the Worker stream as an explicit comparison. The direct path delivered 283.2 Mbps whole-phase and 270.9 Mbps steady, while the Worker path delivered 159.7 Mbps whole-phase and 157.9 Mbps steady. Upload remained around 97 Mbps through either route. Cloudflare’s cache and range-request documentation informed the direct delivery design.[6][7]

That A/B result showed the project’s own Worker path materially limiting download delivery. The browser was measuring both the user’s connection and avoidable server-side response construction.

Steady-state throughput by project-controlled delivery path
Direct R2 object270.9 Mbps steady
Worker stream157.9 Mbps steady
6ed3cdb

Move download testing to first-party static edge assets

Introduced deterministic static payloads, an excluded warm-up, and a Worker fallback.

4bacb7e

Use long-lived segmented Worker responses

Reduced routine request turnover and recorded request generations and lifecycle evidence.

7b27ea8

Add direct R2 download A/B path

Added direct range delivery, validation, cache evidence, and explicit Worker comparison.

8ec174d

Make automatic R2 delivery the default

Retained Worker delivery as a fallback and advanced comparison rather than an assumed equivalent.

The goal was not to force the same number as M-Lab. It was to remove project-created bottlenecks so that disagreement reflected methodology and path differences rather than an inefficient payload implementation.

05

A shared measurement plan

Profiles became contracts rather than loose labels for roughly how long a test runs.

The browser, desktop application, and opt-in CLI Internet-transfer mode consume the same canonical profile contract. It defines idle samples, stage duration, byte ceilings, sample count, aggregate connection count, service checks, comparison allocation, and Stress scaling stages.[9]

Single uses one connection in each direction. Aggregate uses the profile’s configured parallel connection count. Compare runs independent single and aggregate stages so the methods do not compete. Stress measures download scaling at 1, 2, 4, 8, and 10 connections before upload comparison.

Each client computes the actual stage sequence, estimated time, and maximum transfer before starting. Caps are ceilings, not promised consumption. A slower connection reaches the duration first; a fast connection may reach the byte ceiling first.

Native profile duration, transfer ceilings, and diagnostic scope
ProfileBase estimateDownload capUpload capDeep diagnostics
Connection Check15 seconds20 MB8 MBNo
Quick20 seconds600 MB128 MBNo
Full35 seconds900 MB256 MBYes
Stress60 seconds3 GB512 MBYes

06

Throughput and responsiveness

An average stays useful, but the shape of the transfer explains more.

Throughput uses successfully transferred payload bytes over elapsed wall time. Progress is sampled every 250 milliseconds. Intervals shorter than 100 milliseconds are excluded from graph and interval-derived statistics so a few terminal bytes cannot create an impossible peak; those bytes remain in the whole-phase average.

The engine also reports steady-state rate after the initial ramp, retained peak, coefficient of variation, stability, ramp ratio, and classifications such as cap-limited, still-ramping, declining, unstable, or qualified. Multi-sample download stages report medians rather than allowing one outlier to define the profile.

Separate latency requests continue during each transfer stage. Added delay is the loaded median minus the idle median. This shows whether the measured path remains responsive while busy without claiming which router, radio, provider segment, route, or endpoint created the queue.

Single share and parallel gain expose when several flows use materially more of the path than one flow. They do not identify the cause: congestion control, packet loss, endpoint limits, routing, radio conditions, security software, CPU load, and server behavior can all contribute.

Mbps = transferred bytes × 8 ÷ elapsed seconds ÷ 1,000,000

added delay = loaded median - idle median

single share = single steady Mbps ÷ aggregate steady Mbps × 100

parallel gain = (aggregate steady Mbps ÷ single steady Mbps - 1) × 100

07

Endpoint and transport context

A speed result is incomplete without identifying where and how it was measured.

Before a run, the browser and native core probe configured endpoint candidates and select the lowest-latency available origin. Reports preserve candidate success and failure, selected origin, edge location, network name, autonomous system number, negotiated protocol, Transport Layer Security version, and Internet Protocol version when available.[5]

The desktop and CLI can select a source interface. HTTP/1.1, HTTP/2, and LAN sockets bind to the interface’s source address. Managed ICMP, traceroute, DNS, and the exact HTTP/3 request remain routed by the operating system because their portable APIs do not provide equivalent source-interface binding. The report states that scope rather than implying complete isolation.

The native engine sends a separate HTTPS request requiring HTTP version 3.0 exactly. Browsers cannot make an equivalent portable exact-version request, so the web client records the protocol observed through Navigation Timing instead. Observed and forced protocol evidence remain distinct.[8]

08

The native core

Deeper evidence moved into a shared engine instead of being duplicated across a graphical interface and a command line.

NetworkDiagnostics.Core on .NET 10 owns planning, endpoint selection, Internet transfers, ICMP, gateway testing, traceroute, resolver timing, path MTU, TCP and TLS phases, interfaces, Wi-Fi, routes, LAN testing, findings, capability reporting, and serialization.[10]

Avalonia provides the Windows, macOS, and Linux desktop host. The application adds a workbench for profiles, methods, progress, cancellation, comparison, history, import, export, settings, and detailed evidence. The command-line host preserves repeatable deep-only diagnostics and can explicitly opt into the same Internet-transfer plans.

Core

One implementation of native measurement

Planning, transfer enforcement, diagnostics, findings, capabilities, and report serialization.

Desktop

Interactive workbench

Cross-platform native controls, local history, comparison, and evidence-oriented result views.

CLI

Automation and repeatability

Scriptable diagnostics, output paths, interface selection, endpoint overrides, and LAN server/client modes.

Platforms

Five packaged targets

Windows x64, Apple Silicon macOS, Intel macOS, Linux x64, and Linux ARM64.

09

Schema 2.0 and findings parity

Richer evidence did not require incompatible browser and native report silos.

Schema 2.0 is a combined envelope for producer identity, run metadata, transfer plan, endpoint selection, interface scope, network context, Internet measurements, deep diagnostics, optional LAN results, and deterministic findings.

Compatibility remains additive. The browser importer accepts historical native schemas 1.0, 1.1, and 1.2 as well as 2.0. Unknown optional fields are ignored. Missing native sections mean the scope was not run or was unavailable and render as “Not measured,” not failure.

Shared parity fixtures verify that TypeScript and C# findings engines produce the same finding identifiers for representative reports. Rendered wording can remain appropriate to each client while the underlying interpretation does not drift silently.

Imported native JSON is read through the browser File API and parsed locally. It is not uploaded to the project, allowing the browser to act as a compatible report reader without becoming a collection service.

{
  "schemaVersion": "2.0",
  "producer": { "client": "desktop", "engine": ".NET" },
  "plan": { "profile": "full", "method": "compare" },
  "context": {
    "selectedEndpoint": "...",
    "interfaceBinding": "http-and-lan-only",
    "http3": { "attempted": true, "negotiated": "h3" }
  },
  "measurements": {
    "internet": { "...": "..." },
    "deepDiagnostics": { "...": "..." },
    "lan": null
  },
  "findings": [
    { "id": "loaded-latency-high", "confidence": "high" }
  ]
}

10

LAN isolation and interface binding

A second trusted machine removes the public Internet without pretending to remove the local endpoints.

The optional LAN server listens on a configurable TCP port. The client measures request/response latency, generated download traffic, and generated upload traffic with a configurable duration and parallel stream count.

This removes the provider, public transit, and public test platform. It does not remove either machine’s operating system, CPU, adapter, firewall, switch, access point, cabling, or TCP implementation. A preferably wired server with capacity above the expected client rate is recommended.

Fast LAN with slower Internet points away from the local link as the primary bottleneck. Slow LAN makes the local device, adapter, radio, cabling, switch, access point, server, or firewall part of the investigation. It narrows scope rather than producing a universal diagnosis.

11

Privacy and accuracy

Privacy is implemented through minimization, while accuracy begins by stating the remaining dependencies.

The project has no accounts, cookies, analytics, advertising, telemetry, or project-operated result database. Browser history remains in that browser. Native reports remain on the user’s computer. Imported reports are not uploaded.

No retained result does not mean invisible traffic. Cloudflare necessarily processes traffic sent to default endpoints. Full and Stress service checks contact the named providers. User-supplied endpoints and LAN targets remain outside the project’s trust boundary.

Native reports hide local interface addresses, gateways, resolver addresses, private route details, hostname, public IP, MAC address, and service set identifier by default. Enabling local identifiers makes the report sensitive diagnostic material.

Every result describes a particular device, software stack, route, endpoint, and moment. Virtual private networks, security software, CPU load, power saving, Wi-Fi contention, browser scheduling, and background traffic can affect a run. Repeated comparisons are more useful than treating one result as a permanent rating.

12

Validation and release engineering

The project’s contracts are useful only when browsers, native targets, and report readers continue to agree.

Continuous integration validates design-system integration, TypeScript, browser and Worker tests, production builds, the native core, deep-probe tests, desktop builds, profile contracts, report compatibility, and findings parity.

Concurrent native download workers reserve bytes before reading so committed measured payload cannot exceed the configured stage ceiling. Cancellation preserves partial evidence rather than converting an interrupted run into an empty failure.

Desktop and CLI workflows package self-contained artifacts for five runtime targets with checksums and local privacy notes. macOS artifacts are valid application bundles but remain unsigned until Developer ID signing and notarization credentials are configured.

Contracts

Profiles and schemas

Shared fixtures prevent browser and native definitions from drifting independently.

Parity

TypeScript and C# findings

Representative reports must produce the same rule identifiers across engines.

Packaging

Five native targets

Self-contained outputs and checksums make artifacts inspectable and reproducible.

Boundaries

Failures remain explicit

Unsupported, unavailable, partial, and cancelled states are preserved rather than guessed away.

13

Lessons and next work

The project’s largest improvements came from treating disagreement as evidence. M-Lab’s higher result was not dismissed as “a different server,” and it was not used as a number that had to be copied. It triggered a transport investigation that revealed a real bottleneck in the project’s Worker delivery path.

The same pattern shaped the rest of the system. Browser request failures were not relabeled as packet loss. A native probe was added. One aggregate throughput number was not treated as the connection’s full capacity. Single, aggregate, and scaling stages were separated. Missing fields were not guessed. Schema compatibility made absence explicit.

Future work should continue in that direction: more controlled cross-client comparison fixtures, signed and notarized desktop packages, broader platform validation of Wi-Fi and route parsers, and longitudinal comparison tools that help users distinguish a temporary route from a persistent local fault.

A useful diagnostic tool does not need to know everything. It needs to make clear what it observed, what it inferred, and what remains unknown.

References

Primary sources and project records

  1. Measurement Lab, “ndt7 protocol specification.”
  2. Measurement Lab, “NDT (Network Diagnostic Tool).”
  3. Measurement Lab, “ndt7 Protocol and data schema.”
  4. Measurement Lab, “How fast is my Internet? Speed Tests, Accuracy, NDT & M-Lab.”
  5. Measurement Lab, “Locate API.”
  6. Cloudflare, “Enable cache in an R2 bucket.”
  7. Cloudflare, “Default Cache Behavior: client-side range requests.”
  8. World Wide Web Consortium, “Resource Timing.”
  9. Network Diagnostics Suite, “Measurement methodology.”
  10. Network Diagnostics Suite, architecture, profiles, compatibility, privacy, and deployment record.

Part of Network Diagnostics Suite

The report supports the project.
It is not a separate project.

Return to project overview