About tokenspend
tokenspend attributes coding-agent token spend to clients, developers and models. Each developer runs a small shipper on their own machine; this server turns what it sends into a monthly per-client report. To install the shipper, sign in and open the Setup page: it gives you a one-command install for macOS and Linux, and your config for anything else. Source and manual install steps live in the repo.
Privacy
Nothing leaves your machine unless you opt a project in, and what leaves is only numbers. Sessions ship when either:
- their working directory sits under a
.tokenspendmarker file holding a client jobcode. Writing the marker is the consent. - their project's git remote matches an
allowglob in your config, such asgithub.com/sixfeetup/*.
Everything else, including personal projects, scratch directories and repos
with no remote, is counted locally and never sent. A marker containing just
- keeps everything under it home, even inside an allow-listed
repo.
For each shipped session and model, the payload carries token counts
(input, output, cache creation, cache read), the cost in microdollars, the
jobcode or normalized git remote, the model name, and a session key. The
session key is a salted HMAC of agentsview's session id; the salt is generated
once per machine, stored in ~/.config/tokenspend/salt, and never
shipped. Prompts, responses, session titles, file paths, real session ids and
working directories never leave your machine. The shipper only reads paths
locally to find the nearest marker.
The installer gives the shipper its own pinned copy of agentsview with its
own database under ~/.local/share/tokenspend. It never reads or
changes an agentsview you installed for yourself.
You can see exactly what would be sent at any time with
tokenspend-ship --dry-run, and compare shipped against kept-home
totals with tokenspend-ship --report. Neither posts anything.
On the server, everyone who can sign in (sixfeetup Google accounts only)
sees the whole report: every client, expandable to every developer. Developers
can map only remotes they have usage on; mapping one to IGNORED
hides it from admins while its spend still counts in the Ignored total.
How it works
- agentsview indexes your local coding-agent sessions (Claude Code, Codex and the others it supports), including subagent, automated and one-shot sessions.
- The shipper asks agentsview to export sessions active in the last three days, resolves each one to a jobcode marker or an allowed remote, and posts one row per session and model. Each row is the session's cumulative lifetime usage, not a delta.
- The server timestamps every snapshot on arrival and stores it append-only. A snapshot identical to the latest one for its session and model is dropped, so running the shipper any number of times changes nothing.
- The report derives daily spend as the difference between consecutive snapshots of the same session and model, then groups it by client, developer and model. Jobcodes from markers map to their client automatically on arrival; unknown codes and unmapped remotes show up as Unmapped for someone to sort out on the Mappings page.
Because snapshots are cumulative, gaps are harmless: a machine that sleeps
through a weekend loses nothing. After installing, or after placing a new
marker, run tokenspend-ship --backfill once. It re-snapshots
everything since the 1st of the current month and no further, because
backfilled spend lands on the day it is shipped and earlier months are already
invoiced. --lookback DAYS picks an exact window instead.
Updates
Three things can update independently, and none of them disturbs recorded data:
- The shipper updates itself. Before each shipping run it
compares its own file with the copy this server serves at
/setup/ship.pyand replaces itself when they differ, so a change rolls out to every install within a day. That run finishes on the old code; the next run is the new one. An update is only installed if it compiles and is recognizably the shipper. Set"auto_update": falsein~/.config/tokenspend/config.jsonto stay manual; thentokenspend-ship --check-for-updatestells you whether your copy matches the server (exit 1 when it does not),tokenspend-ship --updatereplaces it, and a shipping run ends with an "update available" line on stderr when your copy is stale (--no-nagsilences that). - agentsview. The installer pins a specific version. When the server moves to a newer one, rerun the command from the Setup page; it replaces the private copy and leaves your config and salt alone. If a new version re-parses a session and its totals shrink, the server floors that negative delta at zero rather than subtracting spend.
- The server. Deployed with
kamal deployfrom a commit on main; migrations run on boot. Snapshots are append-only and the report is computed from them on every request, so a server update never rewrites history.