One Needs attention list, with a button for every finding
Today the dashboard and the bell read from two different places. The dashboard's Needs attention is built in the browser from the service and worker lists (app.js:1500-1708); the bell reads /api/collector-alerts and renders its own rows (app.js:4967-5015). So the dashboard can say "Nothing needs you" (app.js:1714) while the bell shows a red count. Several findings have no button at all: a device link (the catalog's device_link, e.g. services/bandwidth/earnapp.yml:47-52) still being retried only becomes a bell notice after a day (app/main.py:1392-1409), a quiet balance gets a muted note, and drift is one row per service per box (app.js:1575-1605).
The proposal: the server builds one list, GET /api/findings, from a new app/findings.py. Each finding has a stable id, a code, a severity, a subject, and typed actions (run, copy, open link, snooze, ignore). The dashboard panel, the bell, the CLI and an agent all read that one list and run the same actions.
Isolation off on a boxToday: Fleet, then Check isolation (2 clicks), then edit .env on the host and restart the worker (3 terminal steps: ssh, edit, docker compose up -d). Nothing on the dashboard. After: 1 click to copy the line, 1 click to re-check. The terminal step stays (it is the host's file) but is one paste.
EarnApp device not on the accountToday: no row while the retry runs; after a day a bell notice (1 click), then select the id by hand, open EarnApp in a new tab and find the page yourself (4+ steps outside the app), and there is no way to ask CashPilot to look again. After: Copy id, Open EarnApp devices, I linked it: 3 clicks.
Catalog drift on 8 copiesToday: 8 rows, one Redeploy each, each with its own confirm (16 clicks), and nothing stops a stateful node from being in the batch. After: 1 click for the 6 safe copies; the 2 Mysterium copies are listed apart.
Out-of-memory stopToday: shows as "Down" with Start (app.js:1545-1555), which restarts into the same limit. Raising the limit means a custom edit or a compose change. After: 1 click: "Raise to 512 MB and restart".
BEFORE
Dashboard today: two sources, no buttons for most findings
screenshot 38-dashboard-with-service.png (top 330 px) on v2.1.1
A"Nothing needs you" is computed in the browser from services and workers only (app.js:1708-1716). Collector alerts, pending payouts and device links live in the bell, which reads /api/collector-alerts (app/main.py:6834).
BThe bell is titled Collector alerts (base.html#notify-toggle) and lists different kinds, with different words, from the panel. It can be red while the panel is green.
CMost bell rows end in text, not a button: a quiet balance gets a note (app.js:4976-4978), a disabled account gets no action there (app.js:5009, showFix is false), an expired device-link retry is a sentence with a URL inside it (app/main.py:1395-1399).
DNo keyboard path: the panel has no shortcut, and the footer keys (g + first letter, /) only jump between pages.
AFTER
Dashboard, Needs attention: one list, grouped by severity, every row has an action
1440 × 900 · row 1 selected with j; frame height grows with the list
Isolation offbox-b 3 earnersEarners on this box can reach your LAN. Its worker runs with CASHPILOT_ISOLATION=off. Set it to on in the worker's .env, restart the worker, then check again.2
Out of memoryRepocket box-aStopped: it ran out of memory at its 256 MB limit 3 times since 11:40. Starting it again with the same limit will stop it again.
Account disabledIPRoyal Pawns 2 boxesThe provider turned this account off, so it earns nothing; $3.18 is the last balance read. Restarting will not help.5
Needs you 2
Not linked yetEarnApp box-cDevice sdk-node-7c1e…3f2 is not on your account yet. CashPilot keeps trying until 14:05. try 4 of 9 · next in 3 minOpen EarnApp devices ↗3
Catalog changed5 services 8 copiesThe catalog changed how these run; a redeploy keeps each copy's own settings. What changes ▾
What changes 4
Network permission added (NET_RAW)
Bitping · box-a, box-b
New image version, CashPilot-built (2.3.0 → 2.4.1)
EarnApp · box-a, box-c
Memory limit set (256 MB)
Honeygain · box-b
Restart policy unless-stopped
PacketStream · box-a
Moves to the isolated networkstateful, done separately
Gone quietTraffmonetizerBalance has not moved for 3 days while 2 copies run. Usually the provider pays late; sometimes the account has no traffic.Open dashboard ↗
gb list · j/k move · Enter primary action · z snooze · . moreSame list: cashpilot finding ls --json
1One count. The panel title, the bell badge and cashpilot finding ls all show 6, because all three read GET /api/findings. "Nothing needs you" can only appear when that list is empty.
2Safety first in the sort: isolation off is Fix now even though nothing has crashed. The fix is a copy, because the setting lives in the worker host's .env (app/lan_fence.py:140); Check again reruns the isolation check and clears the row when it passes.
3A device link shows while CashPilot is still retrying, not only after it gave up a day later. The row says when retries stop, and I linked it, check now runs the retry at once instead of waiting for the backoff (app/main.py:1366-1414).
4Drift is one grouped row with plain-word reasons from app/drift.py (each change gets a code and params). The bulk button skips stateful services; Mysterium keeps its own move steps.
5Every row has the same menu (shown open in the States row below): Snooze 24 h, Snooze 7 days, Ignore (per finding code and subject), Copy as CLI (wave 3). Snoozes are stored server-side so the bell, the CLI and other browsers agree.
6Snoozed findings never vanish: "2 snoozed" opens them, each with Unsnooze. That is the way back out of a snooze.
7The bell shows the same 6 and opens the same list as a dropdown (next frame). Its title changes from "Collector alerts" to "Needs attention".
gb open the list and select the first rowj / k next / previous rowEnter run the primary (filled) actionz snooze 24 h, with an Undo toast. row menuc copy as CLI (wave 3)Esc leave the list
AFTER
The bell: same count, same rows, same keys, from any page
Fleet page in the background · opened with gb outside the dashboard
7Rows that need a choice or a confirm (a bulk redeploy, Stop on all boxes) open the dashboard row with that dialog, so a dropdown never runs a multi-box action on one click.
1Payout questions ("did this payout arrive?") stay in the list as Heads-up with Yes/No buttons; they count, as today's bell does (app.js:659).
AFTER
Redeploy all safe ones: a live progress line that stops on the first failure
left: running · right: stopped on a failure
Catalog changed5 services 8 copies
Redeploying 3 of 6: Honeygain on box-b… pulling 12 / 40 MB
✓Bitping · box-a running, NET_RAW added · 6 s
✓Bitping · box-b running · 5 s
●Honeygain · box-b pulling image
○EarnApp · box-a
○EarnApp · box-c
○PacketStream · box-a
–Mysterium · box-a, box-c stateful, not in this run
One copy at a time, in this order. Leaving the page does not stop it; the bell row shows the same line.
Run stoppedCatalog changed 4 of 6
×Stopped at 4 of 6: EarnApp on box-a failed: the image pull timed out after 120 s. The old container is still running.
✓Bitping · box-a
✓Bitping · box-b
✓Honeygain · box-b
×EarnApp · box-a image pull timed out · old copy kept
○EarnApp · box-c not started
○PacketStream · box-a not started
4The run is sequential and stops at the first failure, so one bad image never takes down a whole service across boxes. A redeploy keeps the old container until the new one starts, which is why the failure line can say "old copy kept".
4Today a redeploy reports kept settings only as a warning toast (app.js:3395-3401); here they are a line under that copy and stay visible after the run.
STATES
Empty, loading, error, the row menu, and after an action
Never "Nothing needs you" until the list has loaded.
Error
UnknownCould not load the list: the server did not answer in 10 s. Showing the list from 14:02.
The bell shows "?" instead of a number, never 0.
Row menu (.)
Account disabled · Pawns
Snooze 24 h z
Snooze 7 days
Ignore for this service
Copy as CLI (wave 3) c
Copy finding id y
After an action
FixedIsolation on for box-b. Checked 14:06; the row leaves in 5 s.
Snoozed IPRoyal Pawns for 24 h
CLI · API · AGENT
The same list for a terminal and for an agent
proposed; there is no cashpilot CLI on main today
terminal
$ cashpilot finding ls
SEV ID WHAT PRIMARYfail isolation_off/box-b Isolation off: earners can reach your LAN copy_env_fix
fail oom/repocket@box-a Repocket stopped: out of memory at 256 MB raise_memory
fail account_disabled/pawns IPRoyal Pawns: account disabled stop_all
warn device_link/earnapp@box-c EarnApp device not on your account yet check_now
warn drift/catalog Catalog changed 5 services (8 copies) redeploy_safe
info quiet/traffmonetizer Balance unchanged for 3 days read_balance6 findings · 2 snoozed (--all to show) · checked 14:02$ cashpilot finding run device_link/earnapp@box-c check_now
✓ EarnApp sdk-node-7c1e…3f2 is on your account. Finding closed.
$ cashpilot finding run drift/catalog redeploy_safe --yes
redeploying 1/6 bitping@box-a … ok
redeploying 2/6 bitping@box-b … ok
redeploying 3/6 honeygain@box-b … ok
redeploying 4/6 earnapp@box-a … failed: image pull timed out (old copy kept)stopped at 4/6; rerun with --from earnapp@box-aexit 7$ cashpilot finding snooze account_disabled/pawns 24h
Snoozed until 2026-11-09 14:07.
Copy as CLI (wave 3; row menu, c) puts this on the clipboard: cashpilot finding run account_disabled/pawns stop_all --yes Exit codes: 0 done · 4 finding gone · 5 needs confirm (no --yes) · 7 stopped on a failure, partly done. An agent reads actions[].confirm and stateful before it runs anything.
1Ids are stable (code/subject), so a snooze, an ignore or an agent's follow-up still points at the same finding on the next poll.
4stateful: true marks actions on nodes whose identity lives in a volume (Mysterium). The CLI refuses them in bulk runs without a per-node flag.