Skip to content

Relay-page bandwidth history: Option B with clickable periods - #231

Draft
1aeo wants to merge 88 commits into
masterfrom
cursor/operator-charts-from-onionoo-f4e1
Draft

Relay-page bandwidth history: Option B with clickable periods#231
1aeo wants to merge 88 commits into
masterfrom
cursor/operator-charts-from-onionoo-f4e1

Conversation

@1aeo

@1aeo 1aeo commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Generate-path charts on each relay page after HTML. Matplotlib stays in a spawn pass; Jinja never imports it.

This revision

Each drawable period is a full Option B hero (identity, throughput, write/read strip, frozen role bands, locked C copy). Family/peer overlays stay 1M-only.

Up to four PNGs per relay, reused as the hero and as CSS-scaled sparks:

  • bandwidth-1m.png
  • bandwidth-6m.png
  • bandwidth-1y.png
  • bandwidth-5y.png

Sparks are links to that period’s own Option B in the same directory (index.html, 6m.html, 1y.html, 5y.html). Thin periods are omitted.

jeangrae 1M Option B hero
jeangrae 6M Option B hero
F3Netze 1M Option B hero
F3Netze 6M Option B hero

Partial merge

Reasonable overlaps only. registry.py is gone. Charts use bandwidth_utils.build_bandwidth_map and time_utils (parse_onionoo_timestamp, published_clock). apply_chart_html_flags lives on relays.py. ChartSpec is inlined in the chart pipeline.

Not merged: matplotlib stays out of Jinja / page_writer; PNG .chart-cache stays separate from the Onionoo JSON cache; series / overlay / outcome / bands stay out of bandwidth_utils; spawn + --chart-workers stay (not the HTML --workers fork pool).

--charts-limit / --fingerprint still slice relays, not periods. --no-charts / missing matplotlib omit History and extra period files.

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

Add a proposal that ranks charts by tor-relays and forum pain points,
plus Allium's own diagnostic catalog, and include mockups generated
from the 2026-08-15 Onionoo details snapshot.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

Comment thread docs/features/planned/charts/generate_onionoo_chart_mockups.py Outdated
Comment thread docs/features/planned/charts/generate_onionoo_chart_mockups.py Outdated
Mock three encodings each of uptime and bandwidth on live Onionoo
series, plus a flag-flapping swimlane. Clarify that the network
scatter and Guard histogram do not belong on every relay page.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
@cursor cursor Bot changed the title Propose top 10 operator charts from live Onionoo data Relay-page chart variations: uptime, bandwidth, flag flapping Aug 15, 2026
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
Replace the invented 95% line with the 98% Guard/HSDir WFU floor,
show the 99.2% month average and the once-only 50% worst bucket on
the plot, and document how #uptime scalars do and do not match.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
…egend

Combine dual-line throughput with the advertised overlay and a
write/read band (0.80–1.25). Move restart and overload into a
color-coded legend. Reserve red for problems only.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Comment thread docs/features/planned/charts/generate_onionoo_chart_mockups.py Outdated
Onionoo only reports when overload was last detected. Restart stays a
point at last_restarted; overload is the proposal-328 band from that
report through +72 hours, matching Allium's existing relay-page window.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
A and B plot every flag equally, so the 4-hour Running gaps that
knock off HSDir are hairline slivers. C is cause→effect, D is
episode counts, E keeps only flags that moved, and F is a status
headline plus the month story.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
Onionoo omits unpublished graphs rather than sending empty series.
Show only what exists (th4r has no 5Y; a 3-day relay has only 1M).
F3Netze is why 5Y matters: 1M is 99.2%, 5Y is 89% with real zeros.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
The chart is authorities seeing Running, not last_restarted. A process
rail makes that obvious. Gray bands are 4-hour buckets where ≥8% of a
400-relay sample also dipped; orange is this relay inside that band,
red is this relay alone. Onionoo bucket math goes in the #uptime info
box and pill tooltips, not on the SVG.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
cursoragent and others added 3 commits August 15, 2026 18:48
The green band is a frozen 0.90–1.15 expected range, not a live
percentile — a network DoS would move p10–p90 and hide the event.
Dashed line is the same-flag-set network median; dotted line is the
AROI/family median. Omit the operator line when the group is one relay.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Onionoo has no overload history graph — only last-detected timestamps.
Consensus files do not carry overload (it lives on descriptors). Until
CollecTor descriptor history exists, charts show a now-badge when the
72h window is still open and leave restart as the only x-axis marker.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Remove the inferred 72h band from R2 A/B/C and top-10 chart 6.
F3Netze shows OVERLOADED NOW because the last report is still inside
the proposal-328 window at relays_published.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
rts = datetime.fromtimestamp(
facts['ratelimits']['timestamp'] / 1000, tz=timezone.utc)
if last_report is None or rts > last_report:
last_report = rts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Badge date ignores rate-limit hit

Medium Severity

last_report pulls any overload_ratelimits.timestamp even when ratelimit_hit is false. General overload correctly gates on general_active, so a present-but-not-hit rate-limit object can overwrite the badge’s “last report” time with a timestamp that did not contribute to current overload.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 30cdf5b. Configure here.

cursoragent and others added 3 commits August 15, 2026 21:08
Census every Onionoo bandwidth graph (1M/6M/1Y/5Y) at 2026-08-15 19:00.
1-month p10–p90 for relays ≥50 KB/s is 0.965–1.130, median 1.021. Longer
periods are tighter. 0.90–1.15 is that 1M shape with a little room — not
a live percentile.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
0.90–1.15 is a typicality band, not an outlier alarm. 1.20 is uncommon
(6.3% of 1M relays, mostly Guards). Red is reserved for <0.80 or >1.50
(2.2% of 1M) — directory authorities and genuinely lopsided relays.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Move the throughput legend into the empty band above the advertised
line so it never covers write/read. Replace the red OVERLOADED NOW
pill with a title-line cue (or a legend diamond). Add three
relay-info.html #bandwidth layout mockups: hero under the heading
(F3Netze), after Capacity/Measurement (jeangrae / 1aeo family), and
a History subsection after Network Participation.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
Comment thread docs/features/planned/charts/generate_relay_page_chart_variations.py Outdated
cursoragent and others added 3 commits August 18, 2026 07:48
Option 3 puts the overload cue in the chart legend, not the heading.
The health-status row still has to say Overloaded — that is the live
page's Stability cell, independent of the chart encoding.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Typical is this flag set’s p10–p90; investigate is beyond its p98.
An Exit at 1.20 is p98.7 (red); a Guard at 1.20 is p91.6 (amber).
Keep the numbers frozen from a quiet census so a network-wide DoS
cannot walk the green band. Drop “check role overlay” — the overlay
is already on the chart as confirmation.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
The strip already uses frozen per-role p10–p90 / p98 bands. The R2
intro and survey note still quoted the rejected global investigate
line; align those sentences with the flag-set thresholds.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
op = overlay_values(ts, overlays.get("operator"))
if op is not None:
axr.plot(ts, op, color=GRAY, linestyle=":", linewidth=1.6, zorder=2)
investigate = (ratio < ilo) | (ratio > ihi)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Investigate band clipped by ylim

Medium Severity

Exit+Guard invest_hi is 1.71, but the ratio strip ylim still tops out at 1.70. The high-side investigate span therefore sits entirely outside the visible axes on the primary F3Netze mockups, while the legend still promises a red zone beyond 1.71. The >p98 label also lands inside the amber shoulder.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 98abf91. Configure here.

cursoragent and others added 17 commits August 22, 2026 09:19
A details published tick must not bust every figure. The cache payload
now stores the derived current_overload_status boolean; the renderer
still receives relays_published as the 72h clock.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Role medians are computed once and family medians once per Onionoo
effective_family. The operator line is omitted when n<2. Contact-group
grouping is gone; --charts-limit and --fingerprint slice chartable
fingerprints in details order.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
auto stays an explicit choice. --charts-limit and repeatable
--fingerprint slice a first run; History img follows that slice.
Chart workers hard-cap at 8; one bad relay cannot kill the pool or
the HTML generate, and a non-zero failed count is printed at the end.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Reuse one selection helper for HTML flags and the spawn pass, drop unused
ChartSpec fields and renderer chrome, and hash the numeric band row instead
of units the figure never reads. Reject non-hex fingerprints at path join,
treat an empty cached PNG as a miss, and default the History img flags off.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Import identity, bands, series, and locked C outcome copy from the
production package. Delete the local dated/verdict branches and the
duplicated parsers.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Collapse the 565-line plan into the current architecture. Cut the
encoding doc's repeated pipeline notes to a pointer; leave the C copy
table, identity, and band spec in place.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
HTML still succeeds without matplotlib: one log line, skip figures,
omit History img. --no-charts turns charts off.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Drop design-history mockups, generators, survey JSON, and encoding essays
so this branch ships only the production chart pass. Keep the pipeline
write-up and point encoding locks at allium.lib.charts.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Walk write/read history into series_by_fp, stash _selection after the HTML
gate, and drive skip/overlay/overload/legend/ratio work from one helper each
so the pass cannot drift from the img flags or re-parse the same blocks.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Add make_relay/make_bw/make_job/stub_chart_pool and fold the omit-img and
pipeline monkeypatches so the DRY cuts stay covered without copy-paste cases.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Review pass: one _overload_fields body, and the chart loop continues
instead of KeyError if a selected fingerprint is absent from series_by_fp.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
series_by_fp now fills a period dict from one bandwidth-map lookup.
Register three spark ChartSpecs that share one renderer name, and add a
slim spark cache payload (period + write/read + advertised, no overlays).

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Reuse _skip_reason, the stashed selection, one spawn pool, and the cache
helpers. The 1M hero is unchanged; sparks omit the write/read strip, C
copy, identity, and family overlays. Missing periods stay off the page.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Assert sparks register, cache keys differ by period, --no-charts omits
them, and a missing 5Y graph does not emit that img.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Reuse the 1M hero renderer for 6M/1Y/5Y, publish four content-hashed
PNGs per relay, and write 6m.html / 1y.html / 5y.html with relative
spark links back to the other period views.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Charts now use build_bandwidth_map from bandwidth_utils and parse
Onionoo timestamps / published_clock from time_utils. The charts
copy of those parsers is gone; existing parse_onionoo_timestamp
callers still get None on bad strings.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
@cursor cursor Bot changed the title Relay-page chart variations: uptime, bandwidth, flag flapping Relay-page bandwidth history: Option B with clickable periods Aug 29, 2026
cursoragent and others added 4 commits August 29, 2026 05:06
apply_chart_html_flags now lives next to other relay_set HTML gates.
pipeline re-exports it for existing tests. ChartSpec and the 1M/6M/1Y/5Y
period heroes are defined in pipeline.py; registry.py is gone.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
series.py no longer re-exports build_bandwidth_map or published_clock.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
The locked 1M figure stays render_relay_bandwidth_1m. Sparks reuse the
same walk, pool, and cache helpers with a slim period payload and a
~40-line drawer (write/read/advertised only). History stays one
index.html slot; missing periods omit that img.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Revert the spark-only drawer that dropped 6m/1y/5y hero pages and
clickable sparks. Restores Option B: each period opens as the hero
with the other three as spark links (four images per relay).

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Comment thread allium/lib/charts/bandwidth.py
cursoragent and others added 6 commits August 29, 2026 05:18
Drop leftover spark-only helpers, one-call wrappers, unused ChartSpec
fields, and a pipeline re-export. Collapse tests that restated the same
cache/CLI/History contracts. Shrink the pipeline note so it no longer
duplicates the code.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Spark clicks now open 6m/1y/5y/index with #bandwidth so the period
page lands on History instead of the document top. Templates still
emit .html paths; the clean-route rewrite turns them into 6m#bandwidth
and ./#bandwidth.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Hero identity is 11pt and titles 12pt so they sit nearer page body
type. Tick labels are 11pt so CSS-scaled sparks stay readable.
renderer_version is 2 so existing chart-cache entries miss. The
write/read legend reuses _event_legend_handles when a restart falls
in span.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Split the chart pass into concurrent 1M/6M/1Y/5Y spawn pools that share
one process budget (default min(CPU, 16), hard cap 16) so a full generate
no longer waits on four mixed workers.

Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Co-authored-by: 1aeo <1aeo@users.noreply.github.com>
Co-authored-by: 1aeo <1aeo@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d9d2e92. Configure here.

import os

fig.canvas.draw()
rgba = _np.asarray(fig.canvas.buffer_rgba(), copy=False)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chart save crashes on NumPy 1

High Severity

_save_trimmed now calls np.asarray(..., copy=False). That copy keyword exists only in NumPy 2.0+, so chart saving raises TypeError on NumPy 1.x. The documented Python 3.8 path (matplotlib&lt;3.8) cannot use NumPy 2, so every PNG write fails there and History images stay broken.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d9d2e92. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants