Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@ by design and would manufacture findings out of geography.

### 🚶 Walk & Test — *survey the building, not just the connection*

Ten destinations people actually depend on — Google, YouTube, Netflix, Facebook and Amazon on the
consumer side; Microsoft 365, Teams, Zoom, Salesforce and Slack on the business side — probed round
after round while you walk the building. Name the spot you are standing in, wait for a few rounds,
Ten destinations people actually depend on — Google, YouTube, Netflix and Amazon on the consumer
side; Microsoft 365, Teams, Zoom, Salesforce, Slack and Atlassian on the business side — probed
round after round while you walk the building. Each gets a card with its own sparkline, and all the
sparklines share one scale so the shapes are comparable at a glance. Name the spot you are standing in, wait for a few rounds,
move, name the next one. The result is a **per-spot comparison**: which corner of the office loses
Teams, where the round trips double, which dead spot is a dead spot for everything and which is
only bad for one destination.
Expand All @@ -111,12 +112,18 @@ What the numbers are, stated permanently on screen rather than in a footnote:
- **These are front doors, not backends.** Netflix playback, Teams call audio and Zoom media run
over paths a browser cannot address, so a green row does not promise a smooth call.
- **Unanswered is not "down", and it is not packet loss.** A timeout, a refused connection, a failed
lookup and being out of range look identical to a browser. The column is called *Answered*.
lookup and being out of range look identical to a browser. So does a blocklist: an ad blocker or a
filtering resolver makes a healthy network look like a dead spot for one destination. The counter
is called *Answered* for that reason, and it is why Meta domains are deliberately not in the list.
- **All ten fire at once each round**, which gives them the same instant — and makes them compete
on a constrained link. Compare rows and spots to each other, not a single figure to a spec sheet.

Each destination's first probe pays for DNS, TCP and TLS, so it is counted but kept out of the
timing statistics. Spots are compared by the **median of each destination's own median**, over only
The opening two rounds and each destination's first answer pay for DNS, TCP and TLS, so they are
counted but kept out of every timing statistic and every chart. Left in, they pinned the top of the
y-axis near a second for the rest of the session and flattened every real measurement into a line
along the bottom. Rounds fire every 1, 2, 3, 5 or 10 seconds; the interval is the pause *between*
rounds, and a round waits for all ten destinations to answer or time out first, so a struggling link
stretches the gap rather than piling requests up. Spots are compared by the **median of each destination's own median**, over only
the destinations that produced a median at *every* spot — pooling raw samples instead would make the
figure lurch when a destination dropped out, reporting a change in which destinations answered as
though it were a change in latency.
Expand Down Expand Up @@ -197,10 +204,7 @@ Handshake timing and application-layer echo round-trips over `ws://` and `wss://
### 10. 🧭 GeoIP & ISP Inspector
Geolocation, ISP, ASN and proxy/VPN signals for an IP or domain, via third-party lookup providers.

### 11. 📊 Live Traffic Monitor
Real-time throughput and latency from the browser's own Performance Timeline.

### 12. 🧭 Edge Path Explorer
### 11. 🧭 Edge Path Explorer
Everything a browser can genuinely observe about the path to a host:

- **Connection phase breakdown** — real DNS → TCP → TLS → time-to-first-byte → transfer timings
Expand All @@ -215,15 +219,15 @@ Everything a browser can genuinely observe about the path to a host:
how far away a server can be. Drawn as a constraint circle: the endpoint is somewhere inside it.
This is a proof, not an estimate — queuing delay only loosens the bound.

### 13. 🗺️ Route Model *(simulated — read this)*
### 12. 🗺️ Route Model *(simulated — read this)*
Resolves a target, looks up its real location, and draws a plausible great-circle path to it.

**The intermediate hops are generated, not measured.** Browsers cannot send ICMP packets or set an
IP TTL, so no web page can perform a real traceroute. The first and last hops are grounded in a real
DNS resolution and a real geolocation lookup; everything between them is illustrative. Exports mark
these records as simulated. Use the Edge Path Explorer above for measurements you can rely on.

### 14. 💾 History & Export
### 13. 💾 History & Export
Results persist in `localStorage`. Search, filter, inspect raw JSON, and export per-tool CSVs, a
master summary, or a bundled ZIP with a manifest.

Expand Down Expand Up @@ -268,9 +272,9 @@ without touching it, so the following go directly from your browser to third par
| `1.1.1.1`, `one.one.one.one`, `dns.quad9.net`, `doh.opendns.com`, `en.wikipedia.org` | Your IP, as latency probe targets, and as the two halves of the resolver test |
| `ipv4.icanhazip.com`, `ipv6.icanhazip.com`, `api4.ipify.org`, `api6.ipify.org` | Your IP, during the dual-stack check — each answers on one address family only |
| `cp.cloudflare.com` | Your IP, during the captive-portal check, and only when NetReady is opened over plain `http` |
| `www.google.com`, `www.youtube.com`, `www.netflix.com`, `www.facebook.com`, `www.amazon.com`, `outlook.office365.com`, `teams.microsoft.com`, `zoom.us`, `login.salesforce.com`, `slack.com` | Your IP, once per round for the length of a Walk & Test run — roughly 200 requests each over ten minutes. One HEAD for a small public file, no cookies sent |
| `www.google.com`, `www.youtube.com`, `www.netflix.com`, `www.amazon.com`, `outlook.office365.com`, `teams.microsoft.com`, `zoom.us`, `login.salesforce.com`, `slack.com`, `www.atlassian.com` | Your IP, once per round for the length of a Walk & Test run — up to ~600 requests each over ten minutes at the one-second interval. One HEAD for a small public file, no cookies sent |
| `api.github.com`, `httpbin.org`, `cloudflare.com`, `dns.google` | Your IP, if you run the HTTP probe against one of its one-click sample targets |
| `stun.l.google.com` and other STUN servers | Your public IP, and potentially local addresses |
| `httpbin.org` | Your IP, only when you press "Trigger Network Spike" on the live traffic monitor |
| `basemaps.cartocdn.com`, `openstreetmap.org` | Map areas you view, revealing an approximate target location |
| Hosts you enter | Direct connections from your browser — that is what a probe *is* |

Expand Down
80 changes: 80 additions & 0 deletions docs/REMOVED-TRAFFIC-MONITOR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Removed: Live Traffic Monitor

Removed from the dashboard on 23 August 2026. This file is the reference record, so
nobody has to go digging through history to find out what it was or why it went.

The code is in git. Last commit that contained it:

```
git show 77305e2:src/components/TrafficMonitor.tsx
```

## What it was

A panel embedded in the dashboard, below the readiness score, that watched the browser's
own resource timeline and charted it live. It contacted nothing on its own except when a
button was pressed.

**How it worked.** A `PerformanceObserver` subscribed to `resource` entries, so every
request the page made for any reason — a speed test transfer, a DNS-over-HTTPS query, a
map tile, a favicon — landed in a buffer as it completed. On a one-second tick the buffer
was drained and aggregated into a `TrafficSample`, and the last 30 samples were kept as a
sliding window.

**What it showed.**

| Element | Content |
|---|---|
| Four KPI tiles | Throughput (Kbps), average latency over the last 5s, requests captured this session, total bytes transferred |
| Throughput sparkline | Kbps per second, 30-second window, cyan area chart |
| Latency sparkline | Average and peak ms per second, 30-second window, two series |
| Resource table | The most recent requests: name, initiator type, duration, transfer size |
| Filter chips | Narrowed the table by initiator type (fetch, img, script, …) |
| Pause / resume | Stopped the aggregation tick, not just the rendering |
| Trigger Network Spike | Made a handful of requests to `httpbin.org` so the sparklines had something real to draw on an idle page |
| Clear | Emptied the buffer and the window |

**What it measured honestly.** Everything it drew came from real Resource Timing entries.
`duration` is readable cross-origin without `Timing-Allow-Origin`, so the latency figures
were genuine even for opaque responses — unlike the phase breakdown, which is not
readable and which this panel never claimed to show. `transferSize` is zeroed for opaque
responses, so the byte totals undercounted cross-origin traffic and the panel did not
say so. That was its one soft edge.

**A bug it once had, worth remembering.** The average latency tile rendered `0 ms` when
no request had completed in the window, because the mean was computed with a `|| 1`
denominator dividing a sum of zeros. That is the exact failure mode `CLAUDE.md` exists to
prevent, and it was caught by an offline browser run rather than by review. Fixed at the
time to render `—` with a reason.

## Why it was removed

It was passive. It observed whatever traffic the page happened to generate, which meant
its numbers described NetReady's own activity rather than the network. On an idle tab it
showed nothing at all, and the honest fix for that — a button that manufactured traffic
so the graph had a shape — is a fair description of the problem: the panel needed to be
fed to look useful.

Walk & Test does the active version of the same idea properly. It probes a fixed list of
destinations on a schedule, so the series means something specific, is comparable between
runs and between places, and is saved. Keeping both would have meant two live-updating
latency charts on adjacent screens measuring different things under similar-looking
labels, and the passive one is the weaker of the two.

## What went with it

- `src/components/TrafficMonitor.tsx`, and its `CapturedResource` and `TrafficSample`
types, which nothing else imported.
- The `httpbin.org` disclosure row that described the Trigger Network Spike button.
`httpbin.org` is still reachable from the app, as one of the HTTP Probe's one-click
sample targets, so the row was rewritten rather than deleted: it now names all four
sample targets and says they are only contacted on a button press. The disclosure list
is a contract, so it has to stay exactly as wide as the app's actual reach — no wider,
and no narrower.

## If it comes back

Two things would be worth fixing first. Report `transferSize` as absent rather than zero
for opaque responses, since an undercount presented as a total is a quiet inaccuracy. And
drop the traffic-generating button: if a panel needs synthetic load to be worth looking
at, the panel is answering a question nobody asked.
4 changes: 0 additions & 4 deletions src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import { displayMetric } from './MetricValue';
import { BottleneckSummary } from './BottleneckSummary';
import { saveHistoryItem } from '../utils/storage';
import { ResponsibleNetworkingModal, isResponsibleNetworkingAccepted } from './ResponsibleNetworkingModal';
import { TrafficMonitor } from './TrafficMonitor';

/**
* One category readiness bar. A category with no measurement behind it shows an
Expand Down Expand Up @@ -369,9 +368,6 @@ export const Dashboard: React.FC<DashboardProps> = ({
</div>
</div>

{/* Real-Time PerformanceObserver Traffic Monitor Sparklines */}
<TrafficMonitor />

{/* Tool Launch Grid */}
<div>
<h2 className="text-sm font-semibold uppercase tracking-wider text-slate-400 mb-4">
Expand Down
12 changes: 6 additions & 6 deletions src/components/PrivacySafetyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ export const THIRD_PARTY_DISCLOSURES: { host: string; receives: string }[] = [
'are still theirs to log.',
},
{
host: 'stun.l.google.com (and other STUN servers)',
receives: 'Your public IP, and potentially local network addresses, during WebRTC analysis.',
host: 'api.github.com, httpbin.org, cloudflare.com, dns.google',
receives:
'Your IP, if you run the HTTP probe against one of the sample targets it offers as ' +
'one-click suggestions. Nothing contacts them unless you press the button.',
},
{
host: 'httpbin.org',
receives:
'Your IP, only if you press “Trigger Network Spike” on the live traffic monitor, which ' +
'makes a handful of requests so the sparklines have something real to draw.',
host: 'stun.l.google.com (and other STUN servers)',
receives: 'Your public IP, and potentially local network addresses, during WebRTC analysis.',
},
{
host: 'basemaps.cartocdn.com',
Expand Down
Loading
Loading