Show raw hop prefixes alongside friendly names in route patterns

Each hop now displays as 'NodeName [ab]' with the hex prefix visible.
Makes prefix collisions obvious — e.g. same prefix resolving to same
name confirms it's a collision, different prefixes confirm real route.
This commit is contained in:
you
2026-03-19 01:21:36 +00:00
parent 8a8800fada
commit c2ebf549a1
3 changed files with 16 additions and 5 deletions
+6 -1
View File
@@ -784,10 +784,15 @@
${data.subpaths.map((s, i) => {
const barW = Math.max(2, Math.round(s.count / maxCount * 100));
const hops = s.path.split(' → ');
const rawHops = s.rawHops || [];
const hasSelfLoop = hops.some((h, j) => j > 0 && h === hops[j - 1]);
const routeDisplay = hops.map((h, j) => {
const prefix = rawHops[j] || '';
return `<span title="${esc(prefix)}">${esc(h)} <span class="hop-prefix">[${esc(prefix)}]</span></span>`;
}).join(' → ');
return `<tr${hasSelfLoop ? ' class="subpath-selfloop"' : ''}>
<td>${i + 1}</td>
<td class="mono" style="white-space:nowrap">${esc(s.path)}${hasSelfLoop ? ' <span title="Contains self-loop — likely 1-byte prefix collision" style="cursor:help">🔄</span>' : ''}</td>
<td class="mono" style="white-space:nowrap">${routeDisplay}${hasSelfLoop ? ' <span title="Contains self-loop — likely 1-byte prefix collision" style="cursor:help">🔄</span>' : ''}</td>
<td>${s.count.toLocaleString()}</td>
<td>${s.pct}%</td>
<td><div style="background:${hasSelfLoop ? '#f59e0b' : 'var(--accent,#3b82f6)'};height:14px;border-radius:3px;width:${barW}%;opacity:0.7"></div></td>
+3
View File
@@ -1150,3 +1150,6 @@ button.ch-item.ch-item-encrypted .ch-badge { filter: grayscale(0.6); }
/* Self-loop subpath rows */
.subpath-selfloop { opacity: 0.6; }
.subpath-selfloop td:first-child::after { content: ''; }
/* Hop prefix in subpath routes */
.hop-prefix { color: #9ca3af; font-size: 0.8em; }
+7 -4
View File
@@ -1137,7 +1137,9 @@ app.get('/api/analytics/subpaths', (req, res) => {
for (let len = minLen; len <= Math.min(maxLen, named.length); len++) {
for (let start = 0; start <= named.length - len; start++) {
const sub = named.slice(start, start + len).join(' → ');
subpathCounts[sub] = (subpathCounts[sub] || 0) + 1;
const raw = hops.slice(start, start + len).join(',');
if (!subpathCounts[sub]) subpathCounts[sub] = { count: 0, raw };
subpathCounts[sub].count++;
}
}
}
@@ -1145,11 +1147,12 @@ app.get('/api/analytics/subpaths', (req, res) => {
// Sort by frequency, return top results
const limit = Number(req.query.limit) || 100;
const ranked = Object.entries(subpathCounts)
.map(([path, count]) => ({
.map(([path, data]) => ({
path,
count,
rawHops: data.raw.split(','),
count: data.count,
hops: path.split(' → ').length,
pct: totalPaths > 0 ? Math.round(count / totalPaths * 1000) / 10 : 0
pct: totalPaths > 0 ? Math.round(data.count / totalPaths * 1000) / 10 : 0
}))
.sort((a, b) => b.count - a.count)
.slice(0, limit);