You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Show a "new version available" badge on each worker in the dashboard when its running RigForge
is older than the latest published RigForge release. Notify-only — the actual upgrade is the separate
[remote-upgrade issue]. This is the low-risk, pithead-only half of the worker-upgrade work and ships
first, with no RigForge dependency.
Why
RigForge and Pithead version independently (RigForge is at v1.9.0 released / 1.10.0 on develop;
Pithead at v1.6.3). Today an operator has no way to see that a rig is running an old RigForge. This
closes that visibility gap. It mirrors the existing stack-level new-release badge (#224).
What already exists (reuse — do not rebuild)
The rig already reports its version: GET :8081/2/summary → rigforge.version is attached to
every worker on the regular poll (service/data_service.py_merge_direct_stats, the w["rigforge"] block).
service/update_checker.py already does the whole check the right way: parse_semver + compute_update (pure, unit-tested), GitHubReleaseClient (fetches latest release over Tor,
fail-silent), UpdateChecker (hourly throttle). Reuse verbatim.
web/views.pybuild_state already surfaces the stack badge as state.update — mirror the shape.
Worker Inspect already renders the version (web/static/workerview.mjs).
service/update_checker.py — add a throttled latest_release_cached(now) accessor returning the
raw {tag, url} (many worker versions vs one shared release).
service/data_service.py — a second UpdateChecker pointed at the RigForge API (one fleet-wide
fetch, hourly-cached, over the existing Tor SOCKS); in the poll loop, attach w["rigforge_update"] = compute_update(w["rigforge"]["version"], tag, url) for rigs that report a
version. Gate on the existingdashboard.check_for_updates flag — one toggle covers both the
stack and rig checks (no second flag).
web/views.py — pass rigforge_update through the whitelist serializers: build_workers (table
row) and build_worker_detail (Inspect).
UI — a small badge in the Workers-Alive table (web/static/components.mjs) and beside the RigForge
version card in Worker Inspect (web/static/workerview.mjs).
Docs (docs/workers.md, house voice) + a wiring test.
Known limitation (document, don't fake)
Only rigs with the sister API (:8081) enabled report a version; a plain-:8080 rig has no version
to compare, so it shows no badge (not a false "up to date").
Acceptance criteria
A worker on an older RigForge surfaces rigforge_update.available in /api/worker and the fleet
state, and the badge renders in both the table and the Inspect header.
Equal / newer / unparseable versions surface no badge.
With dashboard.check_for_updates off, no RigForge release fetch happens and no badge shows.
The RigForge release fetch rides Tor and fails silently offline (no badge, no error surfaced).
make lint + make test green (dashboard ≥ 80%, patch ≥ 90%); house-voice doc pass.
Milestone: v1.10 — Worker upgrade. Ships as its own PR, ahead of the remote-upgrade work.
Implementation facts pinned against rigforge v1.11.2 (2026-07-19)
Version format mismatch is real: the rig reports bare1.11.2
(:8081/2/summary → .rigforge.version, from its VERSION file); RigForge release tags are v1.11.2. Normalize once, at the compute seam — compute_update must not call a rig outdated
because "1.11.2" != "v1.11.2".
The rig deliberately computes no "latest available" itself (SECURITY.md's no-version-ping
promise; _upgrade_check is on-demand CLI only). Our design — ONE fleet-wide, hourly-cached
fetch of the RigForge latest release over Tor — is the correct division of labor, confirmed.
/1/summary and /2/summary serve the same document on current rigs; keep reading whichever
the existing probe already uses.
Testing plan (per docs/testing-strategy.md)
Tier 1: bare-vs-v-prefix normalization; compute_update matrix (older / equal / newer /
missing version / unparseable); badge render states (available, up-to-date, no version reported).
Tier 2 (dashboard): wiring test — poll loop attaches rigforge_update only when the rig
reports a version and dashboard.check_for_updates is on; serializer whitelist carries it to
build_workers/build_worker_detail; the fleet-wide fetch is throttled (one call, many workers)
and fail-silent (stale cache kept).
Follow-up intersecting here: the shared response-size cap for all external HTTP clients (the
update checker this reuses, the XvB clients, the #646 price feed) — tracked separately.
Summary
Show a "new version available" badge on each worker in the dashboard when its running RigForge
is older than the latest published RigForge release. Notify-only — the actual upgrade is the separate
[remote-upgrade issue]. This is the low-risk, pithead-only half of the worker-upgrade work and ships
first, with no RigForge dependency.
Why
RigForge and Pithead version independently (RigForge is at v1.9.0 released / 1.10.0 on develop;
Pithead at v1.6.3). Today an operator has no way to see that a rig is running an old RigForge. This
closes that visibility gap. It mirrors the existing stack-level new-release badge (#224).
What already exists (reuse — do not rebuild)
GET :8081/2/summary→rigforge.versionis attached toevery worker on the regular poll (
service/data_service.py_merge_direct_stats, thew["rigforge"]block).service/update_checker.pyalready does the whole check the right way:parse_semver+compute_update(pure, unit-tested),GitHubReleaseClient(fetches latest release over Tor,fail-silent),
UpdateChecker(hourly throttle). Reuse verbatim.web/views.pybuild_statealready surfaces the stack badge asstate.update— mirror the shape.web/static/workerview.mjs).Scope
config/config.py— addGITHUB_RIGFORGE_RELEASES_API(
.../repos/p2pool-starter-stack/rigforge/releases/latest).service/update_checker.py— add a throttledlatest_release_cached(now)accessor returning theraw
{tag, url}(many worker versions vs one shared release).service/data_service.py— a secondUpdateCheckerpointed at the RigForge API (one fleet-widefetch, hourly-cached, over the existing Tor SOCKS); in the poll loop, attach
w["rigforge_update"] = compute_update(w["rigforge"]["version"], tag, url)for rigs that report aversion. Gate on the existing
dashboard.check_for_updatesflag — one toggle covers both thestack and rig checks (no second flag).
web/views.py— passrigforge_updatethrough the whitelist serializers:build_workers(tablerow) and
build_worker_detail(Inspect).web/static/components.mjs) and beside the RigForgeversion card in Worker Inspect (
web/static/workerview.mjs).docs/workers.md, house voice) + a wiring test.Known limitation (document, don't fake)
Only rigs with the sister API (
:8081) enabled report a version; a plain-:8080rig has no versionto compare, so it shows no badge (not a false "up to date").
Acceptance criteria
rigforge_update.availablein/api/workerand the fleetstate, and the badge renders in both the table and the Inspect header.
dashboard.check_for_updatesoff, no RigForge release fetch happens and no badge shows.make lint+make testgreen (dashboard ≥ 80%, patch ≥ 90%); house-voice doc pass.Milestone: v1.10 — Worker upgrade. Ships as its own PR, ahead of the remote-upgrade work.
Implementation facts pinned against rigforge v1.11.2 (2026-07-19)
1.11.2(
:8081/2/summary → .rigforge.version, from its VERSION file); RigForge release tags arev1.11.2. Normalize once, at the compute seam —compute_updatemust not call a rig outdatedbecause
"1.11.2" != "v1.11.2".promise;
_upgrade_checkis on-demand CLI only). Our design — ONE fleet-wide, hourly-cachedfetch of the RigForge latest release over Tor — is the correct division of labor, confirmed.
/1/summaryand/2/summaryserve the same document on current rigs; keep reading whicheverthe existing probe already uses.
Testing plan (per docs/testing-strategy.md)
compute_updatematrix (older / equal / newer /missing version / unparseable); badge render states (available, up-to-date, no version reported).
rigforge_updateonly when the rigreports a version and
dashboard.check_for_updatesis on; serializer whitelist carries it tobuild_workers/build_worker_detail; the fleet-wide fetch is throttled (one call, many workers)
and fail-silent (stale cache kept).
precondition of that scenario, so no separate bench time.
Follow-up intersecting here: the shared response-size cap for all external HTTP clients (the
update checker this reuses, the XvB clients, the #646 price feed) — tracked separately.