self-hosted one container your machine
UsageFoundry
A self-hosted dashboard and headless run orchestrator for Claude Code.
See where your Pro or Max allowance is actually going — read out of Claude Code's own transcripts at ~/.claude/projects/**/*.jsonl — then spend what is left on agents that run to a budget and stop.
Everything runs on your machine, in one Docker container. No account, no telemetry back to us, no third-party service. The port is published on 127.0.0.1.
what it does
Six things, precisely
Track the allowance
Parses Claude Code's own transcripts into exact token volumes and costs, and reads the real utilisation percentage from the same first-party endpoint
claude /usagecalls.5-hour window, weekly quota, burn rate, projected exhaustion. Breakdowns by model, project, agent, skill and reasoning effort — plus which tools filled those contexts, measured in characters.
Spend it deliberately
Point a run at a folder, give it a task and a budget; it drives
claude -pheadlessly in a loop.The guard is re-checked before every work cycle, and the run stops when the money, the cycles, the clock or the window runs out.
Keep runs out of each other's way
Each run gets its own git worktree and branch, so several agents work one repository at once.
Review the diff, resolve conflicts and merge from the UI.
Chain the work
Saved workflows are graphs of run blocks — including blocks that decide what to run next, blocks that repeat a task until the agent reports done, and blocks that land what the earlier ones built.
Schedulable, under a budget covering the whole graph.
Bring your own tooling
Claude Code plugins in mounted folders are found and listed.
Switching one on puts it on every work cycle from the next one onward.
Drop spent tool output
Winnow is wired in at the two places it can act — an intake filter in front of the API, and a pruner at the work-cycle boundary.
Both ship off. The filter sends a spent tool result in full once, past the last
cache_controlbreakpoint so it is never cached, then a pointer; the pruner edits the transcript at the one moment the edit is free.
01 · usage
Where the allowance went
Claude Code already writes down everything it does. UsageFoundry reads those transcripts where they sit — ~/.claude/projects/**/*.jsonl, from the last byte offset it saw — and turns them into token volumes and costs that are exact, because they are the numbers in the file.
The percentages are a different kind of number. They come from GET /api/oauth/usage, the first-party endpoint claude /usage reads, so they cover the whole account and not just this machine. Three windows: the rolling five-hour session, the week, and the weekly wall scoped to one model family.
Breakdowns by model, project, agent, skill and reasoning effort. Tool composition is measured in characters rather than tokens, because a tool_result carries no usage block — it is a shape, never a cost.
claude /usage reads.Cowork, the desktop app and the web app spend the same limits and write no local transcripts. Work done there is invisible here, so every locally-derived figure is a floor on real consumption. It fails unsafe, and the docs say so rather than smoothing it over.
A window with no ceiling configured renders hatched, not at 0%, and a budget guard written as a fraction of a limit is refused outright rather than assumed.
02 · runs
Spend it against a budget
A run is a folder, a task and a budget. Each work cycle is one claude -p … --output-format stream-json invocation; the loop keeps going until the agent reports done or a guard refuses the next one.
Five of the policy's fields are ceilings: maxIterations, maxRunCostUSD, maxDurationMinutes, maxWeeklyFraction and maxSessionFraction. At least one of the first and third is required: without a monotone terminus the loop has no reason to end.
between-cycles mode.None of the three enforcement modes is a hard cap. Between cycles is the cheapest and overshoots by up to one work cycle; live interrupts the process mid-cycle and throws that cycle’s work away; live-resume interrupts and picks the session back up.
03 · isolation
Several agents, one repository
Every run gets a real git worktree and a branch of its own, cut from your last commit and parked under .uf-worktrees/ inside the mount. Your checkout is not touched, and neither is anybody else's.
Landing is a review step, not a button. The diff is yours to read, the merge is previewed in memory before anything is written, and it refuses to run unless your checkout is clean and still on the branch the run was cut from. Conflicts get resolved on the run’s branch — never in your working copy.
Worktrees keep concurrent runs off each other’s commits, not out of each other’s files. Every run’s checkout is writable by every other run in the same container. The product documents this as a gap rather than as a guarantee.
04 · workflows
Chain the work
A saved workflow is a graph of blocks. A run block is one task in one folder. An orchestrator block takes a brief instead, spends up to ten minutes reading the repository, and starts the runs it decides on — under a fan-out cap you have to set. A repeating block re-runs a task until the agent reports done or the pass cap is reached. A merge block lands what the others built.
One spending limit covers the whole graph, and a workflow can be scheduled. A merge block is never told which branch to land onto — it uses what each run recorded when it started.
05 · plugins
Bring your own tooling
Claude Code plugins already sitting in your mounted folders are found and listed, with what each one actually carries — hooks, agents, skills, commands, an MCP server. Switching one on applies from the next work cycle onward, including runs already in flight. Nothing is installed into ~/.claude.
An agent is a saved role — a name, a description, a prompt and optionally a model — that reaches the CLI as --agents and --agent. Start a run as one and the run answers to a different brief.
06 · context
Stop re-sending spent tool output
Most of a long session is tool output. UsageFoundry wires winnow in at the two places it can act: an intake filter in front of the API, and a pruner — run from a ref pinned into the image — at the work-cycle boundary.
The filter is a loopback proxy the entrypoint starts and points every agent's ANTHROPIC_BASE_URL at. Position is the whole mechanism: a tool result a rule marks as spent goes out in full on the one request the model acts on it, placed past the last cache_control breakpoint so the API never writes it to the cache, and is a pointer on every request after that. It is the full send that sits past the breakpoint — never cached, so never re-read.
The pruner acts between cycles, on a transcript nobody is holding. It removes tool output and keeps the conversation, rather than replacing the conversation with a summary.
Measured in tokens, never bytes: winnow frees more file than it removes from what is actually sent, and its own token readout can report zero for a prune that took out a large fraction of the context.
Cutting context does not automatically save money
The conversation sits in the API's cached prefix, where a read bills at a tenth of the input rate on most models — Claude Fable 5.1 and Claude Mythos 5.1 read at 0.025×, because the multiplier belongs to the model — and editing it invalidates everything after the cut. At a tenth a prune pays 1.9·S − 2·D once and earns 0.1·D a turn back, so it breaks even after 19·(S/D) − 20 further turns. What decides is the ratio, not the size of the session.
Both prunes are gated on that arithmetic, and the boundary is the permissive one: the invalidation is refunded there — the resume was going to rewrite that suffix anyway — so it cuts whenever it cannot predict otherwise. A cycle ended early to make a boundary pays the invalidation in full and is held to a longer horizon. The filter never pays one at all: nothing is edited.
1.9·S − 2·D once and earns 0.1·D a turn back. S/D decides — the same four bars are drawn for a session ten times the size.Both halves ship off. contextPruning is false in the defaults and a blank WINNOW_FILTER is the shipped state, so an install that sets neither behaves exactly as it did before either existed.
Switching pruning on also changes what stops a runaway cycle. It replaced --autocompact 200000, which is no longer on any cycle's argv — so with pruning off, a long cycle runs to the model's whole window, and with it on, the cycle is ended at 200,000 tokens and pruned instead of summarised.
Neither half is spend. The filter's figure reaches no meter, no guard and no window: every meter here is priced from usage frames, and a usage frame reports the request the filter had already rewritten — so the money is already absent from the figures next to it rather than arriving as a credit.
The two also overlap. The product's own card adds them rather than implying they are disjoint, and its own docs/verification.md records what that overstates by: 4.06% of pruned tokens, itself an upper bound.
trust boundary
Self-hosted, and what that costs
There is no account and no service. One container, on your machine, reading files you already have. Nothing leaves it except the calls Claude Code was going to make anyway.
That is also the whole of the isolation. The server starts as root so it can drop each child to a lower uid; a work cycle runs as whoever owns the mounted credentials, because it has to read them. Every folder you mount is inside one blast radius, and a token you put in the environment reaches the agents.
There is a UF_SANDBOX=1 path meant to confine a work cycle further. It is off by default, and the docs are blunt that no work cycle has yet run inside a sandbox that started — one install spent fifteen hours with it set and the sandbox silently not starting. Do not treat it as a security boundary yet.
The server refuses to start without UF_AUTH_TOKEN unless you set UF_ALLOW_NO_AUTH=1, which logs a block at boot and banners every page until you undo it.
quick start
Clone it, fill in two variables, bring it up
git clone https://github.com/Xapicc/UsageFoundry.gitcd UsageFoundrycp .env.example .env# edit .env: UF_WORKSPACE (required — the code you want agents to work on)# UF_AUTH_TOKEN (required: openssl rand -hex 32) docker compose up --buildopen http://localhost:3000
UF_WORKSPACE is the code you want agents to work on. UF_AUTH_TOKEN is a secret you generate — openssl rand -hex 32 is fine. Both are required.
The dashboard has your usage as soon as it can see ~/.claude. Runs need one more step: Settings → Claude account → Sign in, inside the container.
The utilisation percentage is read from Anthropic’s own endpoint, so it covers the whole account. The docs say where each number is exact, where it is estimated, and where it is a guess.
DATA_DIR holds the only copy of your runs, settings, workflows and schedules, and is checked before the server serves anything. docker-compose.yml sets it; if it is not writable the container exits naming the path and the uid.