Remote collectors¶
A collector is a small script that runs on a computer other than the daemon and reports to it. It can report Claude Code's quota from the terminal, what Claude Code is doing in the VS Code panel, and Codex's limits.
Do you need one?¶
| Where Claude Code and Codex run | Use |
|---|---|
| On the same computer as the daemon | Not a collector. The daemon reads them itself: press Set up Claude export on the Claude account, tick the local readers, and Codex works from its own login. No network, no token, and Accept remote reports stays off. |
| On a different computer: the daemon is on a server, in a container, or in another room | A collector, on the computer where Claude Code and Codex run. |
The two are alternatives. Do not set up both for the same account: the admin page warns you about the Claude export and a collector together. If you do both anyway, nothing breaks: the newest reading per window wins.
What a collector is¶
presto_collector.py is a single-file Python 3.8+ script with no
dependencies. One file serves every service that can report remotely, and each
way of reporting is a mode named on the command line:
| Mode | For | Runs |
|---|---|---|
claude-statusline |
Claude Code in a terminal | Claude Code starts it after every response |
watch |
The Claude VS Code panel, and Codex | You start it once and it keeps running |
The daemon's own readers and the collector share their code: the collector file holds a word-for-word copy of the daemon's Claude and Codex readers, so a reading means the same thing whichever way it arrives.
claude-statusline¶
It reads the status-line JSON from standard input, normalises it into the dashboard's report format, and posts it to the daemon. Then it chains your previous status-line command, passing it the same input and printing its output, so nothing is lost.
It never raises, and it always exits 0. If the daemon is unreachable, it gives
up quietly and your status line still works. It is not a service: Claude
Code starts it itself, because the statusLine setting names it.
watch¶
The VS Code panel never runs the status line, and Codex has no hook at all, so
neither can call a collector when something happens. watch looks every
minute instead. For each account in its configuration it reads what the daemon
would read on its own computer, and pushes it with that account's own token:
- Claude: the activity summary from the session logs (last request, model, surface, request and token counts) and the usage cache (the two quota windows, the included credit, and when Claude Code fetched them).
- Codex: the rate-limit windows, the banked reset credits and the credit balance, from the same read-only call the daemon makes.
It is a long-running command, so it must be started: at login, as a service, or in a container. Everything it needs is in one folder.
The collector folder¶
watch reads its settings from ~/.presto-ai-dash/collector.json (on Windows,
C:\Users\you\.presto-ai-dash\collector.json), or from the file named by
--config. Token files sit in the same folder, so one folder holds all of it:
{
"url": "http://192.168.1.50:8787/api/v1/ingest",
"host": "laptop",
"interval_seconds": 60,
"accounts": [
{"service": "claude", "name": "Claude", "token_file": "claude-abc123.token"},
{"service": "codex", "name": "Codex", "token_file": "codex-def456.token"}
]
}
The admin page writes this for you: when you create a remote token it shows the file for that account, with the right token file name. Only the URL needs editing.
| Setting | Meaning |
|---|---|
url |
The daemon's full ingest address, path included. Required. |
host |
The label shown next to a report. Default: this computer's name. At most 32 ASCII characters. |
interval_seconds |
How often to read and push, 30 to 3600. Default 60. |
accounts |
One to eight accounts. Each uses its own token. |
A Claude account takes:
| Setting | Meaning |
|---|---|
token_file |
The file holding this account's remote token. A relative name is taken from the collector folder. Required. |
activity |
Read and push the session-log activity. Default true. |
usage_cache |
Read and push the usage cache. Default true. |
settings_path |
The Claude Code settings file of the profile. Default ~/.claude/settings.json. |
usage_cache_path |
The usage cache file, when it is not where Claude Code puts it. |
A Codex account takes token_file, and optionally executable (a program
named codex), profile_home (an absolute path) and timeout.
An unknown setting is an error, not ignored: a misspelt usage_cache must not
silently turn a reader on or off. Use forward slashes in paths, also on
Windows, so the JSON stays valid.
Trying it¶
makes one round, says what it read and what happened to it for each account,
and exits 0 when every account was sent. Then start it for real:
Run it the way you run any command that should stay up: a login task, a systemd user service, a launch agent, or a container beside Claude. On Windows, run Python directly from the task (a wrapper script can leave the process running when the task is stopped). Nothing it needs is administrator-only.
What reports remotely, and what does not¶
| Reports | By |
|---|---|
| The 5-hour and weekly quota, after a terminal response | claude-statusline |
| Last use, surface, request and token counts, from Claude's session logs | watch |
| The usage cache: the two quota windows and the included credit, with Claude Code's own fetch time | watch |
| Codex limits, reset credits and the credit balance | watch |
It never sends prompts, tool inputs, file paths, account ids, e-mail, organisation, project names or credentials: the fields are the same short whitelist the daemon's own readers keep, and the daemon refuses an envelope with any other field.
What has been tested. The collector, run alone in an empty folder against a throwaway daemon, with Linux and Windows test runs. A real second computer, a real remote Claude session and a real Codex login on another host have not been exercised. If you run it on one, tell the project what you find.
Setting it up¶
Everything remote is on one page: Settings -> Remote reports in the admin page. The account's card says whether remote reports are on, and links there.
- Tick Accept remote reports.
- On the same panel, Download collector. It shows the file's SHA-256: after
you copy the file to the other computer, compare it.
Get-FileHash .\presto_collector.pyon Windows orsha256sum presto_collector.pyelsewhere prints it. - Under Accounts that report remotely, press Create remote token on the account. The token is shown once; the daemon stores only its hash. The page offers Download setup file (check the address in the box above it is one the other computer can reach) and, for Claude, the status-line snippets.
- On the remote computer:
- For the status line (Claude in a terminal): set the
statusLinecommand in that profile's Claude settings file to the snippet, with its URL edited to the daemon's real address. - For
watch(the VS Code panel, Codex): put the downloaded setup file in your Downloads folder (or besidepresto_collector.py) and runpython presto_collector.py setup. It savescollector.jsonand the token in~/.presto-ai-dash/(the token file readable by you only), deletes the setup file, which holds the token, and checks once. Run it again for each further account: they are added to the samecollector.json. Thenpython presto_collector.py watchkeeps it going.
- For the status line (Claude in a terminal): set the
Running it by hand¶
python3 presto_collector.py claude-statusline \
--url http://192.168.1.50:8787/api/v1/ingest \
--token-file ~/.config/presto-ai-dash/claude.token \
--host laptop -- <previous status line command, optional>
--url is the daemon's full ingest address, path included. The collector posts
to exactly what it is given. The token comes from a file, never from the
command line, so a settings file can be shared or synced without leaking it.
On Linux and macOS, chmod 600 the token file: the collector says so once if
others can read it.
If the daemon does not accept the reports¶
Setting a collector up before the daemon is ready is safe. Nothing is stored,
nothing breaks, and watch says what is wrong, once, in plain words, retries
with a wait that grows to five minutes, and says reporting again when it
works. A status line stays silent instead, because it must never get in the way
of Claude Code, so use watch --once to see the reason.
What watch says |
What is wrong | What to do |
|---|---|---|
| the daemon answered 404: remote reports are switched off | Accept remote reports is off on the daemon (or the URL is not its ingest address). While it is off the route answers like any unknown path, with or without a token | Tick it under Settings -> Remote reports, or set DASH_INGEST_ENABLED=1. The collector starts working at its next retry, within five minutes |
| the daemon refused the token (401) | The token was revoked, rotated, or belongs to another account | Create a new remote token for this account and save it in the token file |
| the daemon cannot be reached | Wrong address, a firewall, or the daemon is not running | Check the URL is the daemon's address as this computer sees it (not localhost) and that port 8787 is open; see Securing a deployment |
| the daemon redirected the request | A proxy or a wrong URL. Redirects are never followed, because they would send the token elsewhere | Use the final address |
| the daemon rejected the report (400) | The collector and daemon disagree about the format | Update the older of the two |
| the token file is missing or empty | The file named in collector.json is not there |
Save the token under that name in the collector folder |
| Codex could not be read | Codex is not installed or signed in on this computer | Sign in with codex login there; the collector never signs in for you |
One account failing never stops the others.
If no report arrives¶
- Accept remote reports is ticked. While it is off, the daemon answers as if the route did not exist, with or without a token.
- The URL ends in
/api/v1/ingestand uses the daemon's address as the remote computer sees it, notlocalhost. - The token file holds the token and nothing else, and it is for this account. A new token replaces the old one.
- The remote computer can reach the status port, 8787.
python3(orpythonon Windows) starts Python 3.8 or newer there.- For
watch, runwatch --onceand read what it says.
The Status tab's Remote collectors table shows the last report, the host label and the collector's version for each account. A wrong or revoked token is logged as a failed sign-in, with the address it came from and never the token, and it shows in the Problems list. The Readers table shows a reading that came from a collector, with the host that sent it.
If the collector stops, the daemon notices: a pushed activity summary is treated as unavailable ten minutes after the last one, a pushed Codex reading ages into a stale one, and a usage-cache reading keeps the age Claude Code gave it.
Keeping it up to date¶
The collector has a whole-number version, sent with every report. The daemon compares it with the version of the file it serves. When a copy is older, the daemon says so, as a chore and never as a fault:
- the account's row under Accounts that report remotely names the computer and the two versions;
- the Status tab's Remote collectors table shows the version, and the state becomes update available;
- the Problems list gets one entry per account and old version, and the daemon's log gets the same line once each time it starts;
- the Claude card's detail view on the Presto gets a line,
Collector v1 old, v2 ready.
It changes no status and no colour, and readings keep arriving meanwhile.
To update, download the file again and replace it at the same path. The
status-line command does not change, and Claude Code starts the script afresh
each time. A running watch keeps the old code until you restart it. The
notice clears with the next report.
The daemon never updates the copy for you, and the collector never downloads anything. Either would mean running code fetched over plain HTTP.
A copy whose version is newer than the daemon's is reported as such, which usually means the daemon was downgraded.
How it behaves when the network is bad¶
A status line must never stall your terminal, so claude-statusline is careful:
- It skips a heartbeat if one was sent less than 60 seconds ago.
- It skips an identical report sent less than 60 seconds ago.
- After a network failure it stops posting for five minutes.
- The whole request is bounded by 1.5 seconds.
- Redirects are refused, so the token is never forwarded to another host.
watch has no terminal to protect, so each request may take up to 10 seconds.
After a failure an account waits one interval, then two, four, eight and
sixteen, never more than five minutes; the others carry on. Redirects are
refused here too. A reader that breaks is reported by name and never ends the
watch.
What the daemon does with a report¶
- It validates the body strictly. An unknown key, a bad percentage or a bad
timestamp is refused, and nothing is stored. A source takes only the extra
readings that belong to it: Claude takes
activityandusage_cache, Codex takesreset_creditsandcredits. - It stamps the reading with its own receive time. The remote machine's clock is informational only. The one exception is the usage cache, which keeps the time Claude Code fetched it: that reading may be hours old, and stamping it "now" would show old numbers as new. That time is capped at the receive time, and a reading older than seven days is refused.
- It keeps the previous timestamp when the evidence hash is unchanged, so a redraw is not a new reading. A Codex reading is a fresh look at the provider every time, so each one restarts the clock.
- Claude: a daemon that can read the activity logs or the usage cache on its own machine uses its own reading, and the pushed one fills in when it has none. Codex: a pushed reading under five minutes old is used instead of starting a local Codex; when the collector goes quiet and this computer has no Codex, the last pushed reading is shown, aging into a stale one.
- It records when the collector was last seen and from which host label, and
the card shows
Reports: <host> <age> ago.
One account may report from several computers. The newest reading wins, and the collector trail names the computer that reported last.
Rotating and revoking¶
Under Settings -> Remote reports, the account has Rotate and Revoke.
- Rotating issues a new token and stops the old one working. Save the new one
in the token file; a running
watchreads the file again every round. - Revoking stops that computer's reports immediately. The account keeps its last reading, which then ages into a stale reading rather than a fresh zero.
The security trade-off¶
The route speaks plain HTTP and sits beside the status endpoint. Put the daemon on a private network such as Tailscale, or behind a reverse proxy with TLS, before you turn this on.
A remote token is write-only for one account: it cannot read the status payload, list your sources or touch another account. But the port also serves the status endpoint, so treat its exposure like any other unencrypted service on your LAN.
The collector only reads. It never signs in, signs out, refreshes a login, sends a prompt or spends anything: the Codex call it makes is the same read-only rate-limit call the daemon makes, and Claude's files are opened read-only.