M5 · plan item

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.

Evidence: origin/main d5a98f2 · app/static/js/app.js:1500-1716, 1575-1605, 1666-1690, 4967-5015 · app/main.py:1361-1425, 6834 · app/lan_fence.py:140 · before screenshot 38-dashboard-with-service.png
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
Current dashboard, Needs attention says Nothing needs you

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
CashPilot
+ Add service
$148.62
7

Dashboard

Balance
$148.62
across 11 services
Today
$0.94
+3.1%
This month
$7.48
Containers up
21 / 23

Needs attention 6 1

Checked 14:02 · 3 of 3 boxes reporting · 2 snoozed 6
Fix now 3
Isolation off box-b 3 earners Earners 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 memory Repocket box-a Stopped: 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 disabled IPRoyal Pawns 2 boxes The 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 yet EarnApp box-c Device sdk-node-7c1e…3f2 is not on your account yet. CashPilot keeps trying until 14:05. try 4 of 9 · next in 3 min Open EarnApp devices ↗ 3
Catalog changed 5 services 8 copies The 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-stoppedPacketStream · box-a
Moves to the isolated networkstateful, done separatelyMysterium · box-a, box-c · Move steps →
Heads-up 1
Gone quiet Traffmonetizer Balance has not moved for 3 days while 2 copies run. Usually the provider pays late; sometimes the account has no traffic. Open dashboard ↗
g b 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".
g b 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 g b outside the dashboard
CashPilot
+ Add service
$148.62
Needs attention 6Open on dashboard
Isolation off · box-b
Earners can reach your LAN
Repocket · box-a · out of memory
Stopped at its 256 MB limit
IPRoyal Pawns · account disabled
By the provider; earns nothing
EarnApp · box-c · not linked yet
Retrying until 14:05
Catalog changed · 5 services, 8 copies
6 safe to redeploy, 2 stateful
Traffmonetizer · gone quiet
No balance change for 3 days
j/k Enter z Esccashpilot finding ls --json

Fleet

Boxes 3

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

Empty

Needs attention

Checked 14:06

Nothing needs you. 3 of 3 boxes reporting.

Bell: no badge. 1 snoozed still listed.

Loading (first visit)

Needs attention

Asking 3 boxes…

Never "Nothing needs you" until the list has loaded.

Error

Unknown Could 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

After an action

Fixed Isolation 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 PRIMARY fail 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_balance 6 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-a exit 7 $ cashpilot finding snooze account_disabled/pawns 24h Snoozed until 2026-11-09 14:07.
GET /api/findings · cashpilot finding ls --json
$ cashpilot finding ls --json | jq '.findings[3]' { "id": "device_link/earnapp@box-c", "code": "device_link_pending", "severity": "warn", "subject": { "service": "earnapp", "box": "box-c" }, "title": "EarnApp device is not on your account yet", "params": { "device_id": "sdk-node-7c1e…3f2", "retry_until": "2026-11-08T14:05:00Z", "attempt": 4 }, "actions": [ { "id": "copy_id", "kind": "copy", "text": "sdk-node-7c1e…3f2" }, { "id": "open", "kind": "link", "url": "https://earnapp.com/r/sdk-node-7c1e…3f2" }, { "id": "check_now", "kind": "run", "primary": true, "method": "POST", "path": "/api/findings/{id}/run/check_now", "confirm": false, "stateful": false } ], "snooze": { "allowed": true, "until": null }, "first_seen": "2026-11-08T13:41:00Z" }
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.
AFTER

Mobile, 390 wide

rows stack; the primary action stays full width
CashPilot
+
☰

Needs attention 6

14:02
Fix now 3
Isolation off · box-b Earners on this box can reach your LAN. Set CASHPILOT_ISOLATION=on in the worker's .env.
Out of memory · Repocket box-a · stopped at its 256 MB limit, 3 times since 11:40.
Account disabled · Pawns The provider turned this account off. $3.18 is the last balance read.
Needs you 2
Not linked yet · EarnApp box-c · sdk-node-7c1e…3f2 · retrying until 14:05. EarnApp ↗
Catalog changed · 8 copies 5 services. Mysterium (2) is stateful and done separately. What changes
Heads-up 1
Gone quiet · Traffmonetizer