import type { Metadata } from 'next'; import Link from 'next/link'; import { Bar, LevelLegend, Section } from '@/components/ui/primitives'; import { apiTry } from '@/lib/api'; import { fmt } from '@/lib/format'; import { COMPONENT_LABEL, LEVELS } from '@/lib/pressure'; import type { ComponentId, Methodology } from '@/lib/types'; export const revalidate = 300; export const metadata: Metadata = { title: 'Methodology', description: 'How the Global Internet Pressure Index is computed: components, weights, baselines, robust z, saturation, levels, confidence, Pressure Fronts and self-exclusion.' }; export default async function MethodologyPage() { const m = await apiTry('/api/v1/methodology'); const weights = m?.weights ?? {}; const levels = m?.levels ?? LEVELS.map((l) => ({ max: l.max, id: l.id, label: l.label })); const eng = m?.engine ?? {}; return (

Methodology {m ? · config v{m.version} · {m.updated_at ? `updated ${m.updated_at.slice(0, 10)}` : `source: ${m.source ?? "file"}`} : · live config unavailable, showing documented defaults}

How the pressure is computed

The Global Internet Pressure Index is a composite observability index between 0 and 100 describing how stressed, unstable, congested, degraded or abnormal the public Internet currently is. It is not scientific truth and it is not an uptime percentage: it is a baseline-relative synthesis of independent telemetry, and every number on this site decomposes into the signals that produced it.

  1. Direct measurements from the InternetPressure Observability Network — our probes (HTTP, TCP, TLS, ICMP, DNS, traceroute) toward a registry of representative targets.
  2. Open public Internet telemetry — BGP messages from RIPE RIS Live and RouteViews collectors.
  3. Public raw feeds and polled public infrastructure information.
  4. External APIs and vendor status pages only as optional corroboration (weight 0.05) — the instrument must remain useful if every commercial API disappears.
{Object.entries(weights).map(([id, w]) => ( ))}
Component Weight
{COMPONENT_LABEL[id as ComponentId] ?? id} {fmt(w, 2)}
Σ {fmt(Object.values(weights).reduce((a, b) => a + b, 0), 2)}

Routing — BGP announcements and withdrawals per second, churn, origin-ASN changes, visibility loss, path instability and collector disagreement. Latency — ICMP RTT, TCP connect, TLS handshake, HTTP time-to-first-byte, inter-region latency, packet loss and jitter, always relative to baseline (20 → 100 ms matters; 100 ms alone does not). DNS — lookup latency, SERVFAIL and timeouts, NXDOMAIN anomalies, resolver disagreement across local, Google, Cloudflare and Quad9 resolvers. Availability — representative endpoints failing from at least two probe regions (single-probe failures are the probe's problem, not the Internet's). HTTP/TLS — 5xx, certificate and handshake failures, resets and timeouts. Path — traceroute fingerprints, hop-count deviation and latency shift on changed paths. Corroboration — incidents publicly declared by major providers.

{m?.components && (
Signal recipe per component (from the live configuration)
{Object.entries(m.components).map(([cid, c]) => (

{COMPONENT_LABEL[cid as ComponentId] ?? cid}

    {c.signals.map((s) => (
  • {s.label} {s.id} {fmt(s.weight, 2)}
  • ))}
))}
)}

For every signal and scope we keep a trailing baseline of {String(eng.baseline_days ?? 7)} days, restricted to the same hour of day ±1 h once at least {String(eng.seasonality_min_days ?? 3)} days of history exist (normal daily patterns are not pressure). The most recent {String(eng.baseline_exclude_seconds ?? 600)} s are excluded so an incident cannot baseline itself. Anomaly strength is the robust z-score

z = (x − median) / MAD, clipped to [{String(eng.z_clip_low ?? -3)}, {String(eng.z_clip_high ?? 8)}]

A (probe, target) pair is abnormal above z = {String(eng.z_anomaly ?? 3)}. Signals with fewer than {String(eng.baseline_min_samples ?? 24)} samples are “weak coverage” and their weight is damped: no strong conclusions from thin data. Each component sums its weighted signal stresses and saturates into a 0–100 score with score = 100 × (1 − e^(−{String(eng.saturation_k ?? 0.35)} × stress)), then the index is the weighted sum of component scores. The engine runs every {String(eng.cycle_seconds ?? 10)} s over a {String(eng.window_seconds ?? 120)} s window ({String(eng.bgp_window_seconds ?? 60)} s for BGP rates).

{levels.map((l) => { const c = LEVELS.find((x) => x.id === l.id); return ( ); })}
Level Up to Meaning
{l.max} {l.label}

Each target and ASN carries a Network Importance Score (1–5) reflecting centrality, prefixes, downstream dependency and known services; aggregates weight importance 5 about five times more than importance 1. Event pressure is anomaly strength × affected scope × network importance × confidence. Confidence combines probe count, geographic diversity, signal agreement, BGP corroboration, magnitude, duration and external corroboration. Alongside the index we publish velocity (points per hour), acceleration (per hour²) and volatility so that “high but recovering” is distinguishable from “moderate but worsening”.

A Pressure Front is declared when at least {String(m?.fronts?.min_pairs ?? 3)} source-region → destination-region pairs rise together above z = {String(m?.fronts?.z_threshold ?? 2.5)} with intensity ≥ {String(m?.fronts?.min_intensity ?? 35)}; it is drawn on the map as a great-circle arc with a direction. Incidents open when a component or regional score exceeds {String(m?.events?.detect_threshold ?? 45)} for {String(m?.events?.confirm_cycles ?? 2)} consecutive cycles (detected → developing), become active after {String(m?.events?.active_cycles ?? 6)}, recover below {String(m?.events?.recover_threshold ?? 30)} and resolve after {String(m?.events?.resolve_after_seconds ?? 600)} s continuously below. Correlation is rule-based first — BGP spike + latency + path change + failures raise confidence together — and hypotheses are always phrased as “possible”, “probable” or “high-confidence”.

If fewer than {String(eng.min_probes_for_scoring ?? 2)} probes are fresh (a probe is fresh if it reported within {String(eng.probe_fresh_seconds ?? 180)} s), if the BGP feed is older than {String(eng.bgp_fresh_seconds ?? 120)} s, or if our stores are unhealthy, the engine freezes the index at its last value and flags internal_status = degraded. The interface then shows a visible “Instrument degraded” state with the freeze time instead of interpreting a frozen number. A probe whose targets fail at ≥ {String(eng.probe_local_failure_ratio ?? 0.8)} at once is excluded — its own uplink is down, not the Internet.

Nothing on this site is animated without a measurement behind it: if one update arrives every 10 s, the numbers move every 10 s. We never randomise values or synthesise events, and pages for countries, ASNs, services and incidents exist only where we hold real data.

No scanning, no authentication bypass, no private infrastructure, no exploitation, no excessive traffic, no rate-limit evasion and no personal data. Probes fetch a handful of public endpoints on an adaptive schedule that resembles a normal user, and reveal only an approximate position, provider and ASN. Questions: see the{' '} API page {' '} or write to contact@spboucher.ai.

); }