// Upstream peering map for gohegan.net — the real topology.
//
// AS198657 gohegan.net
// ├─ AS212895 ROUTE64 ── AS215638 Cilix ── { THG → Zayo, RETN → {Sparkle, Telxius, Lumen}, Arelion }
// │ └── AS52025 PARADOX ── { ZET.NET → Orange, Eranium → {Cogent, Lumen}, TATA }
// ├─ AS207841 Inferno ── AS61049 Exascale ── { GTT, NTT, Hurricane, PCCW, DTAG, AT&T }
// ├─ AS11967 Hop179
// └─ AS209735 Lagrange ── { Hurricane, Liberty }
//
// Packets pulse from origin out through the chain along the actual paths.
const VB_W = 216;
const VB_H = 112;
const BOX_W = 18;
const BOX_H = 6.2;
const T1_BOX_W = 17;
const T1_BOX_H = 5.6;
const NODES = {
// Origin
"gohegan.net": {
x: 11,
y: 58,
asn: "198657",
name: "gohegan.net",
primary: true,
},
// Direct transits (gohegan.net's upstreams)
route64: { x: 58, y: 18, asn: "212895", name: "ROUTE64" },
inferno: { x: 58, y: 46, asn: "207841", name: "Inferno" },
hop179: { x: 58, y: 72, asn: "11967", name: "Hop179" },
lagrange: { x: 58, y: 98, asn: "209735", name: "Lagrange" },
// Mid (upstream of upstream)
cilix: { x: 108, y: 13, asn: "215638", name: "Cilix" },
paradox: { x: 108, y: 33, asn: "52025", name: "PARADOX" },
exascale: { x: 108, y: 83, asn: "61049", name: "Exascale" },
// Sub (upstream feeders into Tier 1)
thg: { x: 156, y: 6, asn: "13213", name: "THG" },
retn: { x: 156, y: 19, asn: "9002", name: "RETN" },
zetnet: { x: 156, y: 33, asn: "6204", name: "ZET.NET" },
eranium: { x: 156, y: 46, asn: "35133", name: "Eranium" },
// Tier 1 ISPs — vertical stack at col 4
zayo: { x: 200, y: 6, asn: "6461", name: "Zayo", tier1: true },
sparkle: { x: 200, y: 13, asn: "6762", name: "Sparkle", tier1: true },
arelion: { x: 200, y: 20, asn: "1299", name: "Arelion", tier1: true },
telxius: { x: 200, y: 27, asn: "12956", name: "Telxius", tier1: true },
lumen: { x: 200, y: 34, asn: "3356", name: "Lumen", tier1: true },
orange: { x: 200, y: 41, asn: "5511", name: "Orange", tier1: true },
tata: { x: 200, y: 48, asn: "6453", name: "TATA", tier1: true },
cogent: { x: 200, y: 55, asn: "174", name: "Cogent", tier1: true },
gtt: { x: 200, y: 64, asn: "3257", name: "GTT", tier1: true },
ntt: { x: 200, y: 71, asn: "2914", name: "NTT", tier1: true },
hurricane: { x: 200, y: 78, asn: "6939", name: "Hurricane", tier1: true },
pccw: { x: 200, y: 85, asn: "3491", name: "PCCW", tier1: true },
dtag: { x: 200, y: 92, asn: "3320", name: "DTAG", tier1: true },
att: { x: 200, y: 99, asn: "7018", name: "AT&T", tier1: true },
liberty: { x: 200, y: 106, asn: "6830", name: "Liberty", tier1: true },
};
const EDGES = [
["gohegan.net", "route64"],
["gohegan.net", "inferno"],
["gohegan.net", "hop179"],
["gohegan.net", "lagrange"],
["route64", "cilix"],
["route64", "paradox"],
["inferno", "exascale"],
["lagrange", "hurricane"],
["lagrange", "liberty"],
["cilix", "thg"],
["cilix", "retn"],
["cilix", "arelion"],
["paradox", "zetnet"],
["paradox", "eranium"],
["paradox", "tata"],
["exascale", "gtt"],
["exascale", "ntt"],
["exascale", "hurricane"],
["exascale", "pccw"],
["exascale", "dtag"],
["exascale", "att"],
["thg", "zayo"],
["retn", "sparkle"],
["retn", "telxius"],
["retn", "lumen"],
["zetnet", "orange"],
["eranium", "cogent"],
["eranium", "lumen"],
];
// Full origin-to-tier-1 routes for packet animation.
const PATHS = [
["gohegan.net", "route64", "cilix", "thg", "zayo"],
["gohegan.net", "route64", "cilix", "retn", "sparkle"],
["gohegan.net", "route64", "cilix", "retn", "telxius"],
["gohegan.net", "route64", "cilix", "retn", "lumen"],
["gohegan.net", "route64", "cilix", "arelion"],
["gohegan.net", "route64", "paradox", "zetnet", "orange"],
["gohegan.net", "route64", "paradox", "eranium", "cogent"],
["gohegan.net", "route64", "paradox", "eranium", "lumen"],
["gohegan.net", "route64", "paradox", "tata"],
["gohegan.net", "inferno", "exascale", "gtt"],
["gohegan.net", "inferno", "exascale", "ntt"],
["gohegan.net", "inferno", "exascale", "hurricane"],
["gohegan.net", "inferno", "exascale", "pccw"],
["gohegan.net", "inferno", "exascale", "dtag"],
["gohegan.net", "inferno", "exascale", "att"],
["gohegan.net", "hop179"],
["gohegan.net", "lagrange", "hurricane"],
["gohegan.net", "lagrange", "liberty"],
];
// Edge endpoints: clip to the box edges (not the centers) so lines look right.
const edgeEndpoints = (a, b) => {
const A = NODES[a], B = NODES[b];
const aw = A.tier1 ? T1_BOX_W : BOX_W;
const bw = B.tier1 ? T1_BOX_W : BOX_W;
return {
x1: A.x + aw / 2,
y1: A.y,
x2: B.x - bw / 2,
y2: B.y,
};
};
// Polyline length for a path.
const pathPolyline = (path) => {
const pts = [];
for (let i = 0; i < path.length - 1; i++) {
const e = edgeEndpoints(path[i], path[i + 1]);
if (i === 0) pts.push({ x: e.x1, y: e.y1 });
pts.push({ x: e.x2, y: e.y2 });
}
let total = 0;
const segs = [];
for (let i = 0; i < pts.length - 1; i++) {
const dx = pts[i + 1].x - pts[i].x;
const dy = pts[i + 1].y - pts[i].y;
const len = Math.hypot(dx, dy);
segs.push({ from: pts[i], to: pts[i + 1], len });
total += len;
}
return { pts, segs, total };
};
const PATH_GEOMETRY = PATHS.map(pathPolyline);
const positionAtT = (geom, t) => {
const target = t * geom.total;
let acc = 0;
for (const s of geom.segs) {
if (acc + s.len >= target) {
const u = (target - acc) / s.len;
return {
x: s.from.x + (s.to.x - s.from.x) * u,
y: s.from.y + (s.to.y - s.from.y) * u,
};
}
acc += s.len;
}
const last = geom.segs[geom.segs.length - 1];
return { x: last.to.x, y: last.to.y };
};
const NetworkGraph = ({ animate = true }) => {
const [packets, setPackets] = React.useState(() =>
// Pre-seed with packets at varied progress so the initial frame looks alive.
Array.from({ length: 8 }, () => ({
id: Math.random().toString(36).slice(2),
pathIdx: Math.floor(Math.random() * PATHS.length),
t: Math.random() * 0.95,
duration: 2.4 + Math.random() * 1.6,
tone: Math.random() < 0.22 ? 'accent' : 'fg',
}))
);
const lastSpawnRef = React.useRef(0.4);
const rafRef = React.useRef(0);
React.useEffect(() => {
if (!animate) return;
let last = performance.now();
const step = (now) => {
const dt = Math.min(0.06, (now - last) / 1000);
last = now;
lastSpawnRef.current -= dt;
setPackets(prev => {
let next = prev
.map(p => ({ ...p, t: p.t + dt / p.duration }))
.filter(p => p.t < 1);
while (lastSpawnRef.current <= 0) {
lastSpawnRef.current += 0.18 + Math.random() * 0.32;
if (next.length >= 18) break;
next.push({
id: Math.random().toString(36).slice(2),
pathIdx: Math.floor(Math.random() * PATHS.length),
t: 0,
duration: 2.4 + Math.random() * 1.6,
tone: Math.random() < 0.22 ? 'accent' : 'fg',
});
}
return next;
});
rafRef.current = requestAnimationFrame(step);
};
rafRef.current = requestAnimationFrame(step);
return () => cancelAnimationFrame(rafRef.current);
}, [animate]);
return (
);
};
const NodeBox = ({ node: n }) => {
const w = n.tier1 ? T1_BOX_W : BOX_W;
const h = n.tier1 ? T1_BOX_H : BOX_H;
const fill = n.primary ? 'var(--accent)' : 'var(--bg-0)';
const stroke = n.primary ? 'var(--accent)' : (n.tier1 ? 'var(--accent)' : 'var(--fg-0)');
const asnColor = n.primary ? 'var(--accent-fg)' : 'var(--fg-0)';
const nameColor = n.primary
? 'rgba(255,255,255,0.78)'
: (n.tier1 ? 'var(--fg-1)' : 'var(--fg-2)');
return (
AS{n.asn}
{n.name}
);
};
Object.assign(window, { NetworkGraph });