PeerCortex/public/design-draft-editorial.html
Rene Fichtmueller 96b6ef2d4a feat: MANRS HTML scraping, AS relationships endpoint, rebrand to ASN News
- MANRS: replace broken Observatory API with public participants page scraping
  (www.manrs.org/netops/participants/), 24h cache, returns pass/fail with member count
- /api/validate: add 'relationships' field (upstreams/downstreams/top_peers)
  sourced from RIPE Stat asn-neighbours, no extra API calls needed
- /api/relationships?asn=X: new dedicated endpoint with resolved AS names,
  full upstream/downstream/peer lists sorted by power score, 10min cache
- editorial: rebrand 'The ASN Newspaper' → 'The ASN News' across index-editorial.html
2026-03-30 21:23:42 +02:00

1130 lines
37 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PeerCortex — Craft Editorial Draft</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,700;0,800;0,900;1,700;1,800&family=Source+Serif+4:ital,wght@0,300;0,400;0,600;1,300;1,400&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
/* ── Tokens ───────────────────────────────────────────── */
:root {
--bg: #F5F2EC;
--bg-alt: #EDE9E1;
--bg-inv: #1C1917;
--text: #1C1917;
--muted: #57534E;
--dim: #A8A29E;
--rule: #C9C3B6;
--rule-h: #78716C;
--accent: #B83A1B; /* deep editorial red */
--blue: #1D4ED8;
--green: #15803D;
--amber: #B45309;
--red: #B91C1C;
--serif: 'Playfair Display', Georgia, serif;
--body: 'Source Serif 4', Georgia, serif;
--mono: 'IBM Plex Mono', 'Courier New', monospace;
}
/* ── Reset ────────────────────────────────────────────── */
*{margin:0;padding:0;box-sizing:border-box}
html{font-size:16px}
body{
font-family: var(--body);
background: var(--bg);
color: var(--text);
line-height: 1.65;
-webkit-font-smoothing: antialiased;
}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
/* ── Thin rule utility ────────────────────────────────── */
.rule { border:none; border-top:1px solid var(--rule); margin:0 }
.rule-h { border:none; border-top:2px solid var(--text); margin:0 }
.rule-acc { border:none; border-top:3px solid var(--accent); margin:0 }
/* ══════════════════════════════════════════════════════
MASTHEAD
══════════════════════════════════════════════════════ */
.masthead {
max-width: 1080px;
margin: 0 auto;
padding: 1.25rem 2rem 0;
}
.masthead-top {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
padding-bottom: .75rem;
}
.masthead-name {
font-family: var(--serif);
font-size: 2.4rem;
font-weight: 900;
letter-spacing: -.03em;
color: var(--text);
line-height: 1;
}
.masthead-name sup {
font-size: .85rem;
font-weight: 700;
color: var(--accent);
vertical-align: super;
letter-spacing: .04em;
font-family: var(--mono);
}
.masthead-meta {
font-family: var(--mono);
font-size: .68rem;
color: var(--muted);
text-align: right;
line-height: 1.6;
}
.masthead-nav {
display: flex;
gap: 1.75rem;
padding: .6rem 0;
flex-wrap: wrap;
}
.masthead-nav a {
font-family: var(--body);
font-size: .78rem;
font-weight: 600;
color: var(--muted);
letter-spacing: .04em;
text-transform: uppercase;
text-decoration: none;
transition: color .15s;
}
.masthead-nav a:hover { color: var(--accent) }
/* ══════════════════════════════════════════════════════
SEARCH BAR
══════════════════════════════════════════════════════ */
.search-band {
background: var(--bg-inv);
padding: 1.5rem 0;
margin: .75rem 0 2rem;
}
.search-inner {
max-width: 1080px;
margin: 0 auto;
padding: 0 2rem;
display: flex;
gap: .75rem;
align-items: stretch;
}
.search-label {
font-family: var(--mono);
font-size: .65rem;
font-weight: 600;
color: #D6D0C4;
letter-spacing: .1em;
text-transform: uppercase;
white-space: nowrap;
display: flex;
align-items: center;
}
.search-input {
flex: 1;
background: transparent;
border: none;
border-bottom: 2px solid #57534E;
padding: .4rem .5rem;
font-family: var(--mono);
font-size: 1.1rem;
color: #F5F2EC;
outline: none;
transition: border-color .2s;
letter-spacing: .02em;
}
.search-input:focus { border-color: var(--accent) }
.search-input::placeholder { color: #57534E; font-size: .9rem }
.search-btn {
background: var(--accent);
border: none;
padding: .5rem 1.75rem;
font-family: var(--mono);
font-size: .75rem;
font-weight: 600;
letter-spacing: .08em;
text-transform: uppercase;
color: #fff;
cursor: pointer;
transition: background .15s;
}
.search-btn:hover { background: #9A2F14 }
/* ══════════════════════════════════════════════════════
LAYOUT
══════════════════════════════════════════════════════ */
.page {
max-width: 1080px;
margin: 0 auto;
padding: 0 2rem 4rem;
display: grid;
grid-template-columns: 1fr 300px;
gap: 3rem;
}
@media(max-width:800px){
.page{grid-template-columns:1fr;gap:2rem}
.sidebar{display:none}
}
.main{}
/* ══════════════════════════════════════════════════════
AS HEADER — the "front page article" block
══════════════════════════════════════════════════════ */
.as-header {
padding-bottom: 1.5rem;
margin-bottom: 1.5rem;
}
.as-dateline {
font-family: var(--mono);
font-size: .68rem;
font-weight: 600;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: .6rem;
}
.as-headline {
font-family: var(--serif);
font-size: 3.2rem;
font-weight: 900;
line-height: 1.05;
letter-spacing: -.02em;
color: var(--text);
margin-bottom: .4rem;
}
.as-byline {
font-family: var(--body);
font-size: .85rem;
color: var(--muted);
font-style: italic;
margin-bottom: 1rem;
}
.as-byline strong {
font-style: normal;
font-weight: 600;
color: var(--text);
}
.as-lead {
font-family: var(--body);
font-size: 1.05rem;
font-weight: 300;
color: var(--muted);
line-height: 1.7;
max-width: 64ch;
}
/* AS number pill */
.as-number-pill {
display: inline-flex;
align-items: center;
gap: .5rem;
font-family: var(--mono);
font-size: .75rem;
background: var(--bg-inv);
color: #D6D0C4;
padding: .25rem .75rem;
margin-bottom: 1rem;
letter-spacing: .04em;
}
.as-number-pill .asn { color: #F5F2EC; font-weight: 600; }
/* ══════════════════════════════════════════════════════
SECTION DIVIDER
══════════════════════════════════════════════════════ */
.section {
margin-top: 2rem;
}
.section-head {
display: grid;
grid-template-columns: 1fr auto;
align-items: baseline;
gap: 1rem;
margin-bottom: .75rem;
}
.section-title {
font-family: var(--body);
font-size: .72rem;
font-weight: 600;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--muted);
}
.section-count {
font-family: var(--mono);
font-size: .65rem;
color: var(--dim);
}
/* ══════════════════════════════════════════════════════
PULL QUOTE (large editorial stat)
══════════════════════════════════════════════════════ */
.pull-stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0;
border-top: 3px solid var(--text);
border-bottom: 1px solid var(--rule);
padding: 1.25rem 0;
margin: 1.5rem 0;
}
.pull-stat {
padding: 0 1.5rem 0 0;
border-right: 1px solid var(--rule);
}
.pull-stat:first-child { padding-left: 0 }
.pull-stat:last-child { border-right: none }
.pull-val {
font-family: var(--serif);
font-size: 2.6rem;
font-weight: 800;
line-height: 1;
letter-spacing: -.02em;
color: var(--text);
}
.pull-val.good { color: var(--green) }
.pull-val.warn { color: var(--amber) }
.pull-val.bad { color: var(--red) }
.pull-val.neutral { color: var(--text) }
.pull-label {
font-family: var(--body);
font-size: .7rem;
color: var(--muted);
margin-top: .2rem;
line-height: 1.4;
}
.pull-note {
font-family: var(--mono);
font-size: .6rem;
color: var(--dim);
margin-top: .25rem;
}
/* ══════════════════════════════════════════════════════
VERDICT BLOCK (RPKI / ASPA status)
══════════════════════════════════════════════════════ */
.verdict-row {
display: flex;
gap: 1rem;
flex-wrap: wrap;
margin: 1.25rem 0;
}
.verdict {
display: flex;
align-items: stretch;
gap: 0;
border: 1px solid var(--rule);
}
.verdict-bar {
width: 4px;
}
.verdict-bar.valid { background: var(--green) }
.verdict-bar.invalid { background: var(--red) }
.verdict-bar.unknown { background: var(--dim) }
.verdict-bar.warn { background: var(--amber) }
.verdict-body {
padding: .65rem .9rem;
}
.verdict-label {
font-family: var(--mono);
font-size: .6rem;
letter-spacing: .08em;
text-transform: uppercase;
color: var(--dim);
margin-bottom: .2rem;
}
.verdict-value {
font-family: var(--serif);
font-size: 1rem;
font-weight: 700;
line-height: 1.2;
}
.verdict-value.valid { color: var(--green) }
.verdict-value.invalid { color: var(--red) }
.verdict-value.unknown { color: var(--muted) }
.verdict-value.warn { color: var(--amber) }
.verdict-sub {
font-family: var(--body);
font-size: .7rem;
color: var(--muted);
margin-top: .15rem;
}
/* ══════════════════════════════════════════════════════
DATA TABLE — newspaper style
══════════════════════════════════════════════════════ */
.data-table {
width: 100%;
border-collapse: collapse;
font-family: var(--mono);
font-size: .75rem;
}
.data-table thead tr {
border-top: 2px solid var(--text);
border-bottom: 1px solid var(--rule);
}
.data-table th {
text-align: left;
padding: .45rem .5rem;
font-size: .6rem;
font-weight: 600;
letter-spacing: .08em;
text-transform: uppercase;
color: var(--muted);
font-family: var(--body);
}
.data-table td {
padding: .45rem .5rem;
color: var(--text);
border-bottom: 1px solid var(--rule);
vertical-align: middle;
}
.data-table tbody tr:last-child td { border-bottom: none }
.data-table tbody tr:hover td { background: var(--bg-alt) }
.td-prefix { font-weight: 500; letter-spacing: .02em }
.td-muted { color: var(--muted) }
.td-valid { color: var(--green); font-weight: 500 }
.td-invalid { color: var(--red); font-weight: 500 }
.td-unknown { color: var(--dim) }
.td-link { color: var(--accent); cursor: pointer }
.td-link:hover { text-decoration: underline }
.td-num { text-align: right; tabular-nums; font-variant-numeric: tabular-nums }
/* ══════════════════════════════════════════════════════
HEALTH REPORT (13-check checklist)
══════════════════════════════════════════════════════ */
.health-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
border-top: 1px solid var(--rule);
}
.health-item {
display: flex;
align-items: center;
gap: .75rem;
padding: .6rem 0;
border-bottom: 1px solid var(--rule);
font-size: .78rem;
}
.health-item:nth-child(odd) { padding-right: 1.5rem }
.health-item:nth-child(even) { padding-left: 1.5rem; border-left: 1px solid var(--rule) }
.health-mark {
font-family: var(--mono);
font-size: .7rem;
font-weight: 600;
width: 1.4rem;
height: 1.4rem;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.health-mark.pass { color: var(--green) }
.health-mark.fail { color: var(--red) }
.health-mark.warn { color: var(--amber) }
.health-text { color: var(--text); font-family: var(--body); font-size: .78rem }
.health-sub { color: var(--dim); font-family: var(--mono); font-size: .65rem; margin-top: .1rem }
/* ══════════════════════════════════════════════════════
SIDEBAR
══════════════════════════════════════════════════════ */
.sidebar {
padding-top: 0;
}
.sidebar-block {
padding: 1.25rem 0;
border-top: 2px solid var(--text);
border-bottom: 1px solid var(--rule);
margin-bottom: 1.5rem;
}
.sidebar-block-title {
font-family: var(--body);
font-size: .68rem;
font-weight: 600;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: .9rem;
}
.sidebar-stat {
display: flex;
justify-content: space-between;
align-items: baseline;
padding: .35rem 0;
border-bottom: 1px solid var(--rule);
font-size: .78rem;
}
.sidebar-stat:last-child { border-bottom: none }
.sidebar-stat-label { color: var(--muted); font-family: var(--body) }
.sidebar-stat-val { font-family: var(--mono); font-weight: 500; color: var(--text); font-size: .72rem }
.tag-cloud {
display: flex;
flex-wrap: wrap;
gap: .4rem;
margin-top: .5rem;
}
.tag {
font-family: var(--mono);
font-size: .62rem;
padding: .2rem .55rem;
border: 1px solid var(--rule);
color: var(--muted);
letter-spacing: .03em;
}
.tag:hover { border-color: var(--accent); color: var(--accent); cursor: pointer }
.ext-links {
display: flex;
flex-direction: column;
gap: .5rem;
margin-top: .25rem;
}
.ext-link {
font-family: var(--body);
font-size: .78rem;
color: var(--accent);
display: flex;
align-items: center;
gap: .4rem;
}
.ext-link::before { content: '→'; font-family: var(--mono); font-size: .7rem }
.ext-link:hover { text-decoration: underline }
/* data quality score meter */
.score-meter {
margin: .75rem 0;
}
.score-track {
height: 6px;
background: var(--rule);
position: relative;
margin: .5rem 0;
}
.score-fill {
height: 100%;
background: var(--green);
transition: width .6s ease;
}
.score-label {
display: flex;
justify-content: space-between;
font-family: var(--mono);
font-size: .62rem;
color: var(--dim);
}
/* ══════════════════════════════════════════════════════
COMPARE SECTION
══════════════════════════════════════════════════════ */
.compare-banner {
background: var(--bg-inv);
padding: 1.25rem 2rem;
margin: 2rem -2rem;
display: flex;
align-items: center;
gap: 1.5rem;
flex-wrap: wrap;
}
.compare-label {
font-family: var(--mono);
font-size: .65rem;
letter-spacing: .1em;
text-transform: uppercase;
color: #A8A29E;
white-space: nowrap;
}
.compare-input {
background: transparent;
border: none;
border-bottom: 1px solid #57534E;
padding: .3rem .4rem;
font-family: var(--mono);
font-size: .85rem;
color: #F5F2EC;
outline: none;
width: 130px;
transition: border-color .2s;
}
.compare-input:focus { border-color: var(--accent) }
.compare-input::placeholder { color: #57534E }
.compare-btn {
background: transparent;
border: 1px solid #57534E;
padding: .35rem 1rem;
font-family: var(--mono);
font-size: .68rem;
font-weight: 600;
letter-spacing: .07em;
text-transform: uppercase;
color: #D6D0C4;
cursor: pointer;
transition: all .15s;
}
.compare-btn:hover { border-color: var(--accent); color: var(--accent) }
/* ══════════════════════════════════════════════════════
FOOTER
══════════════════════════════════════════════════════ */
.footer {
max-width: 1080px;
margin: 3rem auto 0;
padding: 1.25rem 2rem 2.5rem;
border-top: 2px solid var(--text);
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
}
.footer-name {
font-family: var(--serif);
font-size: 1.1rem;
font-weight: 800;
color: var(--text);
}
.footer-links {
display: flex;
gap: 1.5rem;
font-family: var(--body);
font-size: .72rem;
color: var(--muted);
}
.footer-links a { color: var(--muted); text-decoration: none }
.footer-links a:hover { color: var(--accent) }
.footer-copy {
font-family: var(--mono);
font-size: .62rem;
color: var(--dim);
width: 100%;
}
/* ── utility ─────────────────────────────────────────── */
.mt0{margin-top:0}.mt1{margin-top:.5rem}.mt2{margin-top:1rem}.mt3{margin-top:1.5rem}
.italic{font-style:italic}
.mono{font-family:var(--mono)}
</style>
</head>
<body>
<!-- ══════════════════════════════════════════════════════
MASTHEAD
══════════════════════════════════════════════════════ -->
<div class="masthead">
<div class="masthead-top">
<div>
<div class="masthead-name">PeerCortex<sup>β</sup></div>
</div>
<div class="masthead-meta">
Network Intelligence<br>
<span class="mono" style="font-size:.62rem;color:var(--dim)">peercortex.org</span>
</div>
</div>
<hr class="rule-h">
<nav class="masthead-nav">
<a href="#">ASN Lookup</a>
<a href="#">RPKI Validator</a>
<a href="#">ASPA Check</a>
<a href="#">Health Report</a>
<a href="#">BGP Routes</a>
<a href="#">Compare</a>
<a href="#">API</a>
</nav>
<hr class="rule">
</div>
<!-- ══════════════════════════════════════════════════════
SEARCH BAND
══════════════════════════════════════════════════════ -->
<div class="search-band">
<div class="search-inner">
<span class="search-label">ASN&nbsp;/&nbsp;Prefix&nbsp;/&nbsp;Org</span>
<input class="search-input" type="text" value="AS15169" placeholder="e.g. AS15169 · 8.8.0.0/24 · Google">
<button class="search-btn">Analyse</button>
</div>
</div>
<!-- ══════════════════════════════════════════════════════
MAIN + SIDEBAR
══════════════════════════════════════════════════════ -->
<div class="page">
<!-- ── MAIN ─────────────────────────────────────────── -->
<main class="main">
<!-- AS Header -->
<div class="as-header">
<div class="as-dateline">Autonomous System Report · Updated 4 minutes ago</div>
<div class="as-number-pill">
<span>ASN</span><span class="asn">AS15169</span>
<span style="color:var(--dim)">·</span>
<span>ARIN</span>
<span style="color:var(--dim)">·</span>
<span>US</span>
</div>
<h1 class="as-headline">Google LLC</h1>
<p class="as-byline">
<strong>GOOGLE-AS,&nbsp;US</strong> &nbsp;·&nbsp; Mountain View, California
&nbsp;·&nbsp; Internet Services Provider
</p>
<hr class="rule-acc" style="margin-bottom:1rem">
<p class="as-lead">
Google LLC operates one of the largest and most interconnected autonomous
systems on the internet, maintaining a global PoP footprint across six
continents. Its routing policy is broadly open, with full RPKI coverage
and a mature ASPA deployment. The network scores in the top 2% of all
tracked ASNs by data quality.
</p>
</div>
<!-- Pull stats -->
<div class="pull-stats">
<div class="pull-stat">
<div class="pull-val good">97</div>
<div class="pull-label">Health Score</div>
<div class="pull-note">13/13 checks pass</div>
</div>
<div class="pull-stat">
<div class="pull-val neutral">4,682</div>
<div class="pull-label">IPv4 Prefixes</div>
<div class="pull-note">announced routes</div>
</div>
<div class="pull-stat">
<div class="pull-val neutral">1,104</div>
<div class="pull-label">IPv6 Prefixes</div>
<div class="pull-note">announced routes</div>
</div>
<div class="pull-stat">
<div class="pull-val neutral">426</div>
<div class="pull-label">Peers</div>
<div class="pull-note">direct sessions</div>
</div>
</div>
<!-- Verdict row -->
<div class="section">
<div class="section-head">
<span class="section-title">Security &amp; Policy</span>
</div>
<hr class="rule" style="margin-bottom:1rem">
<div class="verdict-row">
<div class="verdict">
<div class="verdict-bar valid"></div>
<div class="verdict-body">
<div class="verdict-label">RPKI Origin Validation</div>
<div class="verdict-value valid">VALID</div>
<div class="verdict-sub">4,682 of 4,682 prefixes signed</div>
</div>
</div>
<div class="verdict">
<div class="verdict-bar valid"></div>
<div class="verdict-body">
<div class="verdict-label">ASPA</div>
<div class="verdict-value valid">DEPLOYED</div>
<div class="verdict-sub">RFC 9582 — provider set complete</div>
</div>
</div>
<div class="verdict">
<div class="verdict-bar valid"></div>
<div class="verdict-body">
<div class="verdict-label">IRR Consistency</div>
<div class="verdict-value valid">CONSISTENT</div>
<div class="verdict-sub">ARIN DB matches announced routes</div>
</div>
</div>
<div class="verdict">
<div class="verdict-bar warn"></div>
<div class="verdict-body">
<div class="verdict-label">Route Leak Guard</div>
<div class="verdict-value warn">PARTIAL</div>
<div class="verdict-sub">2 peers without max-prefix</div>
</div>
</div>
</div>
</div>
<!-- Compare banner -->
<div class="compare-banner">
<span class="compare-label">Compare with</span>
<input class="compare-input" type="text" placeholder="AS13335">
<input class="compare-input" type="text" placeholder="AS16509">
<button class="compare-btn">Compare</button>
</div>
<!-- Route prefixes table -->
<div class="section">
<div class="section-head">
<span class="section-title">IPv4 Announced Prefixes</span>
<span class="section-count">showing 8 of 4,682</span>
</div>
<hr class="rule" style="margin-bottom:.5rem">
<table class="data-table">
<thead>
<tr>
<th>Prefix</th>
<th>Description</th>
<th>RPKI</th>
<th>IRR</th>
<th style="text-align:right">Routes</th>
</tr>
</thead>
<tbody>
<tr>
<td class="td-prefix">8.8.8.0/24</td>
<td class="td-muted">Google DNS Anycast</td>
<td class="td-valid">VALID</td>
<td class="td-valid">✓ ARIN</td>
<td class="td-num td-muted">12</td>
</tr>
<tr>
<td class="td-prefix">8.8.4.0/24</td>
<td class="td-muted">Google DNS Anycast</td>
<td class="td-valid">VALID</td>
<td class="td-valid">✓ ARIN</td>
<td class="td-num td-muted">11</td>
</tr>
<tr>
<td class="td-prefix">8.34.208.0/20</td>
<td class="td-muted">GOOGLE-CLOUD-EMEA</td>
<td class="td-valid">VALID</td>
<td class="td-valid">✓ ARIN</td>
<td class="td-num td-muted">9</td>
</tr>
<tr>
<td class="td-prefix">34.64.0.0/10</td>
<td class="td-muted">Google Cloud GFE</td>
<td class="td-valid">VALID</td>
<td class="td-valid">✓ ARIN</td>
<td class="td-num td-muted">18</td>
</tr>
<tr>
<td class="td-prefix">66.102.0.0/20</td>
<td class="td-muted">GOOGLE-FRONTEND</td>
<td class="td-valid">VALID</td>
<td class="td-valid">✓ ARIN</td>
<td class="td-num td-muted">7</td>
</tr>
<tr>
<td class="td-prefix">74.125.0.0/16</td>
<td class="td-muted">GOOGLE-CORP-CORE</td>
<td class="td-valid">VALID</td>
<td class="td-valid">✓ ARIN</td>
<td class="td-num td-muted">24</td>
</tr>
<tr>
<td class="td-prefix">142.250.0.0/15</td>
<td class="td-muted">GOOGLE-EDGE-NODES</td>
<td class="td-unknown">UNKNOWN</td>
<td class="td-valid">✓ ARIN</td>
<td class="td-num td-muted">31</td>
</tr>
<tr>
<td class="td-prefix td-link">172.217.0.0/16</td>
<td class="td-muted">GOOGLE-PROD</td>
<td class="td-valid">VALID</td>
<td class="td-valid">✓ ARIN</td>
<td class="td-num td-muted">19</td>
</tr>
</tbody>
</table>
<div class="mt2" style="font-family:var(--body);font-size:.75rem;color:var(--dim)">
<span class="td-link" style="color:var(--accent);cursor:pointer">Show all 4,682 prefixes →</span>
</div>
</div>
<!-- Health Report -->
<div class="section">
<div class="section-head">
<span class="section-title">Health Report</span>
<span class="section-count">13 of 13 checks passed</span>
</div>
<hr class="rule">
<div class="health-grid">
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">RPKI fully deployed</div>
<div class="health-sub">100% of prefixes have valid ROAs</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">IRR consistent</div>
<div class="health-sub">AS-SET GOOGLE matches route objects</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">No RPKI invalids</div>
<div class="health-sub">0 invalid prefixes detected</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">ASPA record present</div>
<div class="health-sub">RFC 9582 provider authorisation</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">No bogons announced</div>
<div class="health-sub">No RFC 1918 / special-use prefixes</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">No long-lived hijacks</div>
<div class="health-sub">No overlapping prefix conflicts</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">Prefix not too specific</div>
<div class="health-sub">No /25+ IPv4 leaks detected</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">BGP community usage</div>
<div class="health-sub">Informational communities present</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">IPv6 deployed</div>
<div class="health-sub">1,104 IPv6 prefixes announced</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">Route diversity</div>
<div class="health-sub">Multiple upstream paths observed</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">AS path length normal</div>
<div class="health-sub">Avg prepend length ≤ 3</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">PeeringDB present</div>
<div class="health-sub">Record up to date</div>
</div>
</div>
<div class="health-item">
<div class="health-mark pass"></div>
<div>
<div class="health-text">NOC contact reachable</div>
<div class="health-sub">Email + phone verified</div>
</div>
</div>
<div class="health-item" style="visibility:hidden"></div>
</div>
</div>
<!-- Upstream peers table -->
<div class="section">
<div class="section-head">
<span class="section-title">Upstream / Transit</span>
<span class="section-count">top 6 by path frequency</span>
</div>
<hr class="rule" style="margin-bottom:.5rem">
<table class="data-table">
<thead>
<tr>
<th>ASN</th>
<th>Organisation</th>
<th>Relationship</th>
<th style="text-align:right">Path frequency</th>
</tr>
</thead>
<tbody>
<tr>
<td class="td-link td-prefix">AS1273</td>
<td class="td-muted">Vodafone Group PLC</td>
<td><span style="font-family:var(--mono);font-size:.65rem;color:var(--blue)">TRANSIT</span></td>
<td class="td-num td-muted">34%</td>
</tr>
<tr>
<td class="td-link td-prefix">AS174</td>
<td class="td-muted">Cogent Communications</td>
<td><span style="font-family:var(--mono);font-size:.65rem;color:var(--blue)">TRANSIT</span></td>
<td class="td-num td-muted">28%</td>
</tr>
<tr>
<td class="td-link td-prefix">AS3257</td>
<td class="td-muted">GTT Communications</td>
<td><span style="font-family:var(--mono);font-size:.65rem;color:var(--green)">PEER</span></td>
<td class="td-num td-muted">17%</td>
</tr>
<tr>
<td class="td-link td-prefix">AS1239</td>
<td class="td-muted">Sprint / T-Mobile</td>
<td><span style="font-family:var(--mono);font-size:.65rem;color:var(--green)">PEER</span></td>
<td class="td-num td-muted">12%</td>
</tr>
<tr>
<td class="td-link td-prefix">AS6453</td>
<td class="td-muted">TATA Communications</td>
<td><span style="font-family:var(--mono);font-size:.65rem;color:var(--blue)">TRANSIT</span></td>
<td class="td-num td-muted">6%</td>
</tr>
<tr>
<td class="td-link td-prefix">AS3356</td>
<td class="td-muted">Lumen Technologies</td>
<td><span style="font-family:var(--mono);font-size:.65rem;color:var(--green)">PEER</span></td>
<td class="td-num td-muted">3%</td>
</tr>
</tbody>
</table>
</div>
</main>
<!-- ── SIDEBAR ────────────────────────────────────────── -->
<aside class="sidebar">
<!-- Identity block -->
<div class="sidebar-block">
<div class="sidebar-block-title">Registration</div>
<div class="sidebar-stat">
<span class="sidebar-stat-label">ASN</span>
<span class="sidebar-stat-val">AS15169</span>
</div>
<div class="sidebar-stat">
<span class="sidebar-stat-label">Registry</span>
<span class="sidebar-stat-val">ARIN</span>
</div>
<div class="sidebar-stat">
<span class="sidebar-stat-label">Country</span>
<span class="sidebar-stat-val">US · Mountain View</span>
</div>
<div class="sidebar-stat">
<span class="sidebar-stat-label">Type</span>
<span class="sidebar-stat-val">Content / ISP</span>
</div>
<div class="sidebar-stat">
<span class="sidebar-stat-label">Registered</span>
<span class="sidebar-stat-val">2000-03-30</span>
</div>
<div class="sidebar-stat">
<span class="sidebar-stat-label">PeeringDB</span>
<span class="sidebar-stat-val td-link" style="color:var(--accent)">Open policy</span>
</div>
</div>
<!-- Data Quality score -->
<div class="sidebar-block">
<div class="sidebar-block-title">Data Quality</div>
<div class="score-meter">
<div style="font-family:var(--serif);font-size:2.2rem;font-weight:800;color:var(--green);line-height:1">
97<span style="font-size:1rem;color:var(--muted);font-weight:400">/100</span>
</div>
<div class="score-track mt1">
<div class="score-fill" style="width:97%"></div>
</div>
<div class="score-label">
<span>0</span><span>50</span><span>100</span>
</div>
</div>
<div style="font-family:var(--body);font-size:.72rem;color:var(--muted);font-style:italic;margin-top:.5rem">
Composite score across 5 data sources: ARIN, RIPE, RADb, BGPstream, PeeringDB.
</div>
</div>
<!-- Communities -->
<div class="sidebar-block">
<div class="sidebar-block-title">BGP Communities</div>
<div class="tag-cloud">
<span class="tag">15169:10</span>
<span class="tag">15169:30</span>
<span class="tag">15169:45</span>
<span class="tag">15169:70</span>
<span class="tag">65000:10</span>
<span class="tag">65100:1</span>
<span class="tag">65100:3</span>
</div>
</div>
<!-- External Links -->
<div class="sidebar-block">
<div class="sidebar-block-title">External Sources</div>
<div class="ext-links">
<a class="ext-link" href="#">BGP.he.net / AS15169</a>
<a class="ext-link" href="#">RIPE Stat</a>
<a class="ext-link" href="#">PeeringDB</a>
<a class="ext-link" href="#">bgproutes.io</a>
<a class="ext-link" href="#">RPKI Validator (Cloudflare)</a>
<a class="ext-link" href="#">ARIN Whois</a>
<a class="ext-link" href="#">BGPlay (RIPE NCC)</a>
</div>
</div>
<!-- ASPA template -->
<div class="sidebar-block">
<div class="sidebar-block-title">ASPA Record</div>
<div style="background:var(--bg-inv);padding:.9rem;font-family:var(--mono);font-size:.62rem;color:#9CA3AF;line-height:1.8;margin-top:.5rem">
<span style="color:#D6D0C4">ASPA</span>: {<br>
&nbsp;&nbsp;<span style="color:#A8A29E">customer-as</span>: <span style="color:#F5F2EC">15169</span>,<br>
&nbsp;&nbsp;<span style="color:#A8A29E">provider-set</span>: [<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#86EFAC">1273</span>,&nbsp;<span style="color:#86EFAC">174</span>,<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#86EFAC">3257</span><br>
&nbsp;&nbsp;]<br>
}
</div>
</div>
</aside>
</div><!-- end .page -->
<!-- ══════════════════════════════════════════════════════
FOOTER
══════════════════════════════════════════════════════ -->
<div class="footer">
<span class="footer-name">PeerCortex</span>
<nav class="footer-links">
<a href="#">API</a>
<a href="#">GitHub</a>
<a href="#">MCP Server</a>
<a href="#">Changelog</a>
<a href="#">Imprint</a>
</nav>
<div class="footer-copy">
Data sourced from RIPE NCC, ARIN, RADb, BGPstream, Cloudflare RPKI, PeeringDB.
Cached results may be up to 30 minutes old.
Not affiliated with any registry or operator. Open source under MIT.
</div>
</div>
</body>
</html>