M7 · Settings › API & CLI tokens, agent setup, copy as CLI (wave 3)

Mockup on origin/main · v2.1.1 · default theme “Classic night” · fake data
Today before
Settings page today: earnings collection, environment, about. No API section.
Settings today (templates/settings.html:9-15): four sections, none for API access. The fleet key shows under Environment as “Fleet API Key … Not set”.
  • !
    The only scripting key lives in the environment. CASHPILOT_ADMIN_API_KEY resolves to role owner (app/auth.py:177-179), so a Home Assistant widget or an agent gets full control, including stored credentials and volume deletion. No scopes, no expiry, no box limit, no last-used.
  • !
    Create or rotate a key = 3 terminal steps: edit .env or the compose file, docker compose up -d, update every client. Nothing in the UI. Revoking one client means rotating everyone.
  • !
    No schema, no CLI. OpenAPI and /docs are switched off (app/main.py:2315-2317). An agent reads app.js to learn the routes: api() is a bare fetch wrapper (app/static/js/app.js:14-38) with nothing to record or copy.
  • !
    The Fleet card calls the enrolment key “API Key” (templates/fleet.html:138-155: “Reveal API Key”, CASHPILOT_API_KEY). People paste it into scripts; it is the worker key, role fleet (app/auth.py:180-182).
  • →
    After: create a scoped token in 3 clicks + typing a name; revoke in 2 clicks; no restart. Copy a working CLI or curl line for anything you just did in 2 clicks.
Settings › API & CLI after 1440×900 · tokens present, one just used
CashPilot
+ Add service$41.87O

Settings

1

Personal tokens Create token n

A token lets a script, a dashboard widget or an AI agent act as you, limited to the scopes and boxes you pick. Each one is shown once, stored hashed, and can be revoked here without a restart.

NameScopesBoxesExpiresLast used
home-assistant
cp_hA2x_…f9
readallnever4 min ago · GET /api/earningsRevoke
claude-code
cp_Qm7d_…k2
readoperateproxiesbox-abox-bin 90 days · 6 Feb 2027just now · POST /api/services/honeygain/restartRevoke
old-grafana
cp_Zt0p_…a1
readallin 3 days19 days agoExtend Revoke
Admin key legacy, set in .env
CASHPILOT_ADMIN_API_KEY
admin (everything)allnever12 days agoHow to retire it
2
3

Use from a terminal

The cashpilot CLI talks to this dashboard over the same API. Every command takes --json, --dry-run on anything that changes a box, and exits non-zero on refusal.

Install, on any machine
$ uvx cashpilot --versionCopy
Or, with no install, inside the dashboard container
$ docker exec cashpilot-ui cashpilot statusCopy
Sign in once (the token is read from stdin, never from argv or history)
$ cashpilot login --url http://cashpilot.local:8080 --token-stdinCopy

The URL above is this dashboard's own address, filled in for you. CLI reference

4

Use from an AI agent

Claude Code and other agents work best with a token scoped to what you want them to touch, plus the guide below. Destructive calls return a plan first; the agent must send confirm to carry it out.

MCP server config
{ "mcpServers": { "cashpilot": { "command": "uvx", "args": ["cashpilot", "mcp"], "env": { "CASHPILOT_URL": "http://cashpilot.local:8080", "CASHPILOT_TOKEN": "paste the claude-code token" } } } }Copy
Or the Claude Code one-liner
$ claude mcp add cashpilot -e CASHPILOT_URL=http://cashpilot.local:8080 -- uvx cashpilot mcpCopy
5

Your last 10 actions as commands kept in this browser only · secrets masked

Do it once with clicks, then copy it to a script. Values from credential fields are replaced with $VARIABLE names.

10:42cashpilot service restart honeygain --box box-aCopy CLI Copy curl
10:39cashpilot egress test res-pool-1 --box box-bCopy CLI Copy curl
10:37cashpilot egress add res-pool-1 --url-stdin <<< $PROXY_URLCopy CLI Copy curl
10:31cashpilot service deploy earnapp --box box-b --env-file earnapp.envCopy CLI Copy curl
09:58cashpilot service stop storj --box box-b --yesCopy CLI Copy curl
+ 5 more · Show all · Clear
Create token: dialog, keyboard flow and the four states states
Keyboard: focus starts in Name. Tab walks scopes → expiry → boxes → buttons and stays inside the dialog. Space toggles a scope, ←→ moves the expiry radio, Enter in a field submits, Esc closes and returns focus to “Create token”.
7
successtoken shown once
Token claude-code created. Copy it now: CashPilot stores only a hash and cannot show it again.
cp_Qm7d_N4x0VfR2sTq8LwY1bHc6ZpE3uJk2Copy
Try it
curl -s -H "Authorization: Bearer $CASHPILOT_TOKEN" \ http://cashpilot.local:8080/api/workers | jq '.[].name'Copy
Copy is focused on open, so Enter copies. On plain-http LAN origins the clipboard API is blocked; the box becomes a selected read-only field, the same fallback Fleet uses (fleet.html:150-153).
emptyfirst visit
No tokens yet. Scripts and agents use a token instead of your password.
Create token Read the agents guide
loading
error
Could not create the token: a token named claude-code already exists. Pick another name or revoke the old one. 409
The error sits under the Name field and moves focus there, instead of a toast that disappears.
Copy as CLI everywhere you act (wave 3), and the Fleet rename after
8
Dashboard › service row › ⋯ menu
Honeygainbox-a$3.12⋯
Copied cashpilot service logs honeygain --box box-a
↑↓ moves, the letter jumps, Esc closes back to ⋯. Today each row has separate icon buttons and no copy (app.js:2676-2746).
9
Fleet › Add proxy dialog (bottom)
Collapsed by default, remembers open/closed. Same block in Deploy, Run proxied copy and Add box. Route: POST /api/proxies (fleet.html:1101).
10
Fleet › Add a box
CASHPILOT_UI_URL=http://cashpilot.local:8080 CASHPILOT_API_KEY=•••••••• CASHPILOT_WORKER_NAME=box-c
setReveal API Key → Reveal enrolment keyCopy
This key only enrols workers. For scripts and agents, create a token in Settings › API & CLI.
Rename in fleet.html:143 (and :774) and the hint at :155. The env var name stays CASHPILOT_API_KEY for compatibility; the label says what it is.
The same thing from a terminal and for an agent after
  zsh — cashpilot
$ pbpaste | cashpilot login --url http://cashpilot.local:8080 --token-stdin
Signed in as token claude-code (read, operate, proxies · boxes box-a, box-b · expires 2027-02-06)
Saved to ~/.config/cashpilot/config.toml (mode 600)

$ cashpilot status --json
{
  "version": "2.2.0",
  "boxes": [
    { "name": "box-a", "online": true, "running": 6, "isolation": "on" },
    { "name": "box-b", "online": true, "running": 4, "isolation": "off" }
  ],
  "findings": [
    { "id": "down/earnapp@box-b", "severity": "fail" }
  ],
  "balance_usd": 41.87
}

$ cashpilot service restart honeygain --box box-a
✓ honeygain restarting on box-a

$ cashpilot service rm storj --box box-b
✗ refused: token claude-code lacks scope "deploy"
  Ask the owner to add it in Settings › API & CLI.
$ echo $?
3
# exit codes: 0 ok · 1 error · 2 usage · 3 auth or scope · 4 not found
#             5 refused for safety (incl. no --yes) · 6 box offline · 7 partial · 8 conflict
docs/agents.md rendered · also served as /llms.txtCopy as Markdown

Driving CashPilot from an agent

Ask the owner for a token with the smallest scopes that do the job. Never ask for admin to restart a service.

ScopeLets youCLI
readstatus, earnings, logs, fleetstatus · service ls|logs · finding ls
operatestart, stop, restartservice start|stop|restart
deploydeploy, redeploy, removeservice deploy|redeploy|rm
proxiesproxies and proxied copiesegress add|import|test · copy apply
adminsettings, credentials, tokenstoken ls|create|revoke · box add

Stateful services: plan, then confirm

Storj, Mysterium and other nodes with an identity volume never lose data by default. Every remove, stop or redeploy returns a plan first:

# 1. read the plan cashpilot service rm mysterium --box box-a --dry-run --json # 2. remove, keeping the identity volume cashpilot service rm mysterium --box box-a --yes # 3. only if the owner asked: delete the volume too cashpilot service rm mysterium --box box-a --yes --delete-volume=mysterium

The volume flag must repeat the slug, the same check as the dialog in M8. Over HTTP, add dry_run=1 to the same request: it returns {"plan": …, "plan_id": "…"}; send plan_id back within 5 minutes.

Evidence on origin/main