confirm() focuses OK, so two Enter presses delete the data volume with allow_delete_critical=true. For a Mysterium node that is the identity and its earnings history; for Storj, the node and its held amount.confirm() calls in the client (app.js ×15, fleet.html ×5, activity.html ×2, import.js ×1, counted with git grep -n "confirm("). None shows what will happen, none can name a volume, none is themed or announced to screen readers beyond the browser default, and the text cannot be selected or copied..modal-overlay divs (style.css:730-745): no focus trap, the page behind stays tabbable (walkthrough shot 35-kbd-modal.png: Tab leaves the dialog into the catalog rows).CP.confirm(plan) on a native <dialog>. Focus lands on Cancel; keeping data is the default; deleting needs the slug typed. Removing and keeping the volume: Tab, Enter (2 keys, deliberate). Deleting: 3 arrow/tab keys + type the slug + Enter.This is what will happen:
cashpilot-mysterium stops and is deletedcashpilot_mysterium_data (12 MB)inert: Tab and the screen reader cannot reach the rows.<dialog> opened with showModal(): the browser makes the page inert, traps Tab, closes on Esc and restores focus. Replaces the .modal-overlay divs (style.css:730-745) and every confirm() through one CP.confirm({title, plan, choices, danger}) in a new confirm.js.DELETE /api/services/mysterium?box=box-a&dry_run=1 returns the container, the volume with its size, and the backup state already tracked by the payout registry (app/main.py:6558-6569). Keep is pre-selected.cashpilot_mysterium_datamysterium ✓CancelDelete volumecashpilot_storj_dataaria-live="polite" region, so it is read aloud. It stays while hovered or focused (today toasts vanish on a timer, app.js:44-69). Tab reaches it; Enter starts the node again.What changes compared with the running container:
cashpilot_mysterium_dataallow_delete_critical, app/main.py:3841-3856). Honeygain still stops in one click. “Restart instead” is the action most people wanted.$ cashpilot service rm mysterium --box box-a --dry-run --json { "action": "remove", "service": "mysterium", "box": "box-a", "stateful": true, "steps": [ { "what": "container", "name": "cashpilot-mysterium", "effect": "remove" }, { "what": "volume", "name": "cashpilot_mysterium_data", "size_bytes": 12582912, "effect": "keep", "delete_with": "--delete-volume=mysterium" } ], "backup": { "confirmed_at": "2026-11-05T09:14:00Z", "current": true }, "plan_id": "pln_8c1e4a", "expires_in": 300 } $ cashpilot service rm mysterium --box box-a Remove Mysterium from box-a? container cashpilot-mysterium remove volume cashpilot_mysterium_data keep (12 MB, backed up 3 d ago) Not a terminal and no --yes: nothing done. $ echo $? 5 # needs --yes $ cashpilot service rm mysterium --box box-a --yes --delete-volume=storj ✗ --delete-volume must name this service (mysterium). Nothing done. exit 2 (usage)
--dry-run --json and the MCP tool all render the same object from _svc_remove(…, dry_run=True) next to the existing delete_volumes path (app/main.py:3837-3892).--yes the CLI prints the plan and exits 5. On a TTY it asks, with “No” as the default ([y/N]), the same safe default as the dialog.plan_id; the destructive call must send it back. An agent cannot skip the plan, and the owner sees the plan in Activity.| Where | Today asks | Becomes | Default focus |
|---|---|---|---|
| app.js:4172, :4174 | Remove custom service from box; then “Also delete its data volume?” | Frame 1 plan, Keep default, type slug to delete | Cancel |
| app.js:2750 | Remove {slug}? stop and delete the container | Frame 1 plan (volume kept unless chosen) | Cancel |
| app.js:2687 (none) | Stop, no confirm | Frame 2 for stateful services only | Cancel |
| app.js:2710 | Stop {name} on N containers? | Frame 2, lists the boxes, warns per stateful one | Cancel |
| app.js:3736, :3760, :4139 | Redeploy … with its saved settings? | Frame 3 diff plan | Cancel |
| fleet.html:533, :551 | Redeploy behind the LAN fence / turn on isolation | Frame 3 diff plan (network changes) | Cancel |
| fleet.html:793 | Remove worker | Plan: what stays on the box, how to re-enrol | Cancel |
| fleet.html:1134, :1144 | Remove proxied copy / delete proxy | Plan: copies using this proxy, Remove | Cancel |
| app.js:2737, import.js:205 | Forget a deployment / imported settings | Short dialog, no plan (nothing on a box changes) | Forget |
| app.js:845, :929, :4155, :4467, :4684, :3320, :3725; activity.html:327, :345 | Cash-out, payout, custom definition, credentials, deploy-anyway warning, self-update, ignore drift, activity records | Short dialog with the record named; destructive label | Cancel |