Track Claude Code & Codex Spend
@marginfront/code-cost-clarity is a small command-line tool. One command wires your coding-agent usage telemetry (from Claude Code, Codex, or both) through a local collector and into MarginFront, priced per developer, per model, with accurate prompt-cache token splitting.
This is a guardrail, not a hard limit. It tracks spend and can pause work on machines where it’s installed. It can’t stop charges at Anthropic or OpenAI, and it won’t catch a machine that never installed it. For a hard ceiling, set a spend limit in your Anthropic Console or OpenAI account. You’re responsible for your actual provider charges.
Want a hard ceiling? See Set a hard spend ceiling for Codex at OpenAI.
By default this is internal cost visibility, not billing. Your company watches its own AI spend (per developer, per model). It doesn’t charge developers. Out of the box it doesn’t cut anyone off either: turning on spend caps is a separate, opt-in step (see Spend Control below).
This tool never reads, needs, or transmits your Anthropic or OpenAI API key. The only credential it uses is your MarginFront key, and only to send usage to MarginFront.
Beta (pilot software). Code Cost Clarity is in active development. Pin a version, expect rough edges, and report issues.
On Windows? Start with Code Cost Clarity on Windows. This page describes the macOS setup. Windows takes the same commands and gets the same background meter that starts at every sign-in, but a few details differ, including the desktop apps, which aren’t metered there yet. The Windows page has all of them.
Usenpx, notnpm install. This is a CLI tool, not a library. Installing it into a project pulls in its dependencies and can surface unrelated audit warnings. Run it withnpx @marginfront/code-cost-clarity@latest init(the@latestskips a stale npx cache). Thenpm i ...box on npmjs.com is npm’s auto-generated default for every package and is not the intended usage here.
What this does (one sentence)
Every time a developer runs Claude Code or Codex, this tool captures the token usage and sends it to MarginFront as a usage event, so you can see who used what, on which model, and how much it cost.The mental model (read this first)
Think of it like a cash-register receipt system:- Claude Code (and Codex) is the register. As it works, it broadcasts receipts: how many tokens, which model, which developer.
- The collector (
otelcol-contrib, open source) is the catcher. It runs in the background, catches the receipts, and writes them to a file. - The forwarder (the glue inside this tool) reads those receipts and sends each one to MarginFront.
- MarginFront records the event, prices it, and shows it under the developer’s email.
OTEL_METRIC_EXPORT_INTERVAL) plus a final flush when the session ends. It ships at 5 minutes by default (batched, so you get roughly one event per turn). Lower it (60 seconds or 5 seconds) for a more live drip, or raise it (10 minutes) to batch harder. The collector keeps repeated reports from double-counting no matter the interval.
Quick start (macOS)
On Windows the commands are the same, but a few details around them differ. Follow Code Cost Clarity on Windows instead. Get your MarginFront key first. Log in at app.marginfront.com, then go to Build -> API keys -> Create key pair and copy the secret key (mf_sk_...). init will ask you to paste it.
source, no second terminal. Your spend shows up in MarginFront under your developer email, automatically, and the background meter restarts itself at every login. Windows gets the same background meter; inside a WSL distro there’s none, so you keep run open instead.
Already running Claude or Codex? Quit and reopen them after init (and start a fresh terminal session). The desktop apps and each terminal session read the telemetry config when they launch, so anything that was already open won’t emit until it’s reopened.
There is noCheck the meter anytime:code-cost-claritycommand on its own. Installing it does not add a new command to your computer, so typingcode-cost-clarity statusby itself says “command not found” and nothing is wrong. Every command runs throughnpx, like the ones on this page.
init, run npx @marginfront/code-cost-clarity run to stream your spend in the terminal (Ctrl-C stops it, and shuts its collector down too). You’ll see a line per turn like:
What gets captured, and what doesn’t
This meters the coding agents: it reads theclaude_code.* and codex.* telemetry that Claude Code and Codex emit to the local collector. init wires telemetry into both places these agents look, so it captures them in the terminal and the desktop apps:
- Terminal / CLI (
claude,codex) and local IDE coding sessions read the config from~/.claude/settings.jsonand~/.codex/config.toml. - The Claude Code and Codex desktop apps don’t read that config block, and a Dock-launched app never sees your shell, so on macOS
initalso writes the same vars into the launchd GUI-session environment, which Dock-launched apps inherit. Quit and reopen a desktop app afterinitso it reads them at launch. The desktop apps aren’t covered on Windows yet; run your coding agent from a terminal there.
- The Claude consumer chat app (the chat window). That’s the general assistant, not Claude Code, so it never emits the coding-agent telemetry this reads.
- Anything in a cloud sandbox or not connected to your machine: a cloud session, or a remote / devcontainer / SSH setup where the agent can’t reach the local collector at
127.0.0.1:4318. If a surface doesn’t run on your machine and reach the local collector, this can’t see it.
The commands
The background meter below is a launchd job on macOS and a Task Scheduler logon task on Windows. Inside a WSL distro there’s no background meter, soinit finishes cleanly without installing one, and start, stop, and status have no meter to act on there. run, preview, and uninstall behave the same everywhere.
| Command | What it does |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------|
| init | Creates ~/.marginfront-ccc/, writes your settings and the collector config, downloads the collector binary, and installs a background meter that starts at login (a launchd job on macOS, a Task Scheduler logon task on Windows). Wires telemetry into Claude Code and Codex, including the desktop apps on macOS (via the launchd GUI-session env). On a normal terminal it also asks you to paste your MarginFront secret key (mf_sk_...), checks it with MarginFront before saving, and saves it for you (nothing to hand-edit). A key the server rejects (wrong, revoked, or a publishable mf_pk_... key) gets a plain no and a fresh prompt instead of being saved. If your team shares one Claude/Codex login, it also asks which developer this machine’s cost belongs to (see Sharing one login across the team). Safe to re-run: it keeps a working saved key and never overwrites it, but if that saved key has stopped working it re-checks and asks you for a fresh one instead of skipping past the one thing that needs fixing. Inside a WSL distro it also finishes cleanly (there’s no background meter to install there); see the Windows page. Add --no-prompt on a server or in a script. Add --control to also turn on spend caps (see Spend Control). |
| status | Shows whether the background meter is running, plus recent activity. If your key has stopped working, it says so plainly: a red Key line with when sending stopped, a note that the meter is still recording but not sending, and a count of the turns waiting in the queue to go out. |
| start | Starts (or reinstalls) the background meter. init does this for you; use it to bring the meter back after stop. |
| preview <capture.json> | Prints the exact record it would send for one captured snapshot (Claude Code or Codex). Needs no MarginFront API key, so it’s great for a dry run. |
| run | Optional live terminal view. Starts the collector in the foreground and streams your spend to the terminal until Ctrl-C (Claude Code, Codex, or both, no extra flag). The background meter from init already does this without a terminal; use run when you want to watch it live. Add --fold-cache only for an unpriced model (see below). |
| stop | Pauses the background meter (and any foreground run). start brings it back. Inside a WSL distro, where there’s no background meter, stop still ends a running foreground forwarder and clears a stale pid file, which is exactly what run tells you to do when it reports one already running. On Windows, stop shuts the collector down hard rather than asking it to wrap up first, so usage from the seconds right before it can go unsent. |
| work ["<name>"] | Puts this machine’s spend on a piece of work (a project, a ticket, a client). With a name it sets it; with no name it prints the one in effect; --clear removes it. Nothing to restart. Same as editing the CCC_WORK_ID line by hand (see Label this machine’s spend with a work item). |
| done "<name>" [n] | Counts something you finished, so the ROI page can show what one cost. done "paper reviewed" records one; done "paper reviewed" 3 records three. A finished thing is priced at zero, so it never moves your spend. Finished-work counting is off until you turn it on (see Count what you finish). |
| uninstall | Stops everything (the background meter and the collector), reverts the telemetry config it wrote (Claude settings.json, the Codex [otel] block, the spend-cap hook and check script if you turned Control on, and the desktop GUI-session env), and deletes the collector binary and runtime files (reclaims the ~360 MB). Keeps your settings. Add --purge to also delete your settings and MarginFront API key. |
| help, version | The usual. |
Spend Control (opt-in caps)
By default this tool only watches spend. Turn on Spend Control and it can stop a Claude Code or Codex tool call when your team’s admin-set cap is reached. What it does. Your admin sets a spend cap in MarginFront (for example, “$200 per developer per week”), on the dashboard’s Internal AI Spend page or through the Spend Controls API. With Control on, this tool installs a tiny check that Claude Code and Codex both run before every tool call. If the cap is reached, the tool call is stopped with a message that names the cap and tells you to contact your admin for a raise. It runs on the same spend numbers you already meter; nothing new leaves your machine. Turn it on:y to arm it. For Claude Code, it arms on your next claude session (or a /hooks reload). For Codex there’s one extra step, and only you can do it: open codex, run /hooks, and approve the MarginFront cap check. Codex skips hooks nobody has reviewed, so nothing changes there until you do. After arming, cap changes from your admin apply even to sessions already running.
Turn it off:
- It fails open. A missing, unreadable, or out-of-date cap file never blocks you. A broken cap should never brick your machine, so it doesn’t.
- It stops tool calls, not tokens. The prompt that proposed a blocked call is already billed. What the cap kills is unbounded agentic work: the runaway sessions that produce surprise bills.
- Caps in track mode never block. They only watch and send alert emails. Only a cap in enforce mode that covers all AI tools stops anything on your machine; a cap scoped to a single provider is charted but doesn’t block.
- Per-developer caps need to know who the developer is. A whole-team cap enforces on every machine. A cap aimed at one developer enforces only on machines where
CCC_DEVELOPER_EMAILnames that developer (see Sharing one login across the team). The tool never guesses whose machine this is, because guessing wrong would block the wrong person. - It pauses Claude Code and Codex. Gemini and xAI spend still counts toward every cap, but the device cap doesn’t pause those tools.
- Hosted tools are never paused. A tool that runs on the provider’s servers (like built-in web search) never takes the local tool path, so the check never sees it.
- An unapproved Codex hook is inert. Until you approve it in
/hooks, Codex silently skips it.ccc statustells you which state you’re in, and it says “armed” only when the cap can actually stop something. - “Your cap” vs. “the model refused.” If the model itself declines a request (its own safety layer), that isn’t this cap. The block message says so, so you can tell the two apart.
- Requires
python3on macOS and Linux (standard on both). Windows doesn’t need it: the check runs on Node, which the tool already needs.
Per-developer attribution is automatic
What makes “who spent what” work is the developer’s email, and Claude Code puts it in the telemetry on its own. It’s the developer’s logged-in Claude account email. There is no manual email setup. Each developer just loads the telemetry settings and runs Claude Code normally. Works whether the developer signs in with an org-managed seat or an interactive login. On org-managed Claude seats the email is stamped for free. If a developer’s sign-in doesn’t surface an email, the tool doesn’t drop the usage. It attributes it to a clearly labeled placeholder customer (claude-code-no-identity) and prints how to fix it (sign in with an org-managed seat, or attach a customer mapping). You’ll see the placeholder in preview or run output if it ever kicks in.
Codex attributes the same way, with one wrinkle: whether your email shows up depends on how you sign in to Codex (an org-managed seat or an OpenAI API-key sign-in stamps it; some interactive ChatGPT logins may not). That sign-in is between you and OpenAI; this tool never sees your OpenAI key. See Also capture Codex.
Your MarginFront API key is separate from the developer’s identity: it’s the tool’s own credential for posting to MarginFront.runneeds it;previewdoes not.
Sharing one login across the team
Some teams share one Claude or Codex login across the whole team. When that happens, every developer’s telemetry carries the same email, so all the AI cost piles onto one person and per-developer attribution breaks. This tool fixes that without any change to how you sign in, because it runs locally on each laptop, so each laptop can carry its own developer identity. Duringinit it asks one question: which developer this machine’s AI cost belongs to. The question is pre-filled with your global git email (git config --global user.email), which is usually the right person sitting at this laptop. You have three choices:
- Press Enter to accept the pre-filled email.
- Type a different email to attribute this machine to someone else.
- Clear it and leave it blank to auto-detect from whatever email the coding-agent login reports.
CCC_DEVELOPER_EMAIL in ~/.marginfront-ccc/.env (mode 600, right next to your key). To change it later, edit that one line and then stop and start (or re-run init).
Which email wins, highest first:
CCC_DEVELOPER_EMAIL(this machine’s developer). When set, it wins for every record this machine sends: Claude and Codex, token turns and tool calls.- Otherwise the email the coding-agent login reports.
- Otherwise the no-identity placeholder (
claude-code-no-identityorcodex-no-identity).
This is internal cost visibility only.CCC_DEVELOPER_EMAILre-labels which developer the cost shows up under in your MarginFront. It does not change who pays or any customer’s bill. The--no-promptinstall (server or script) skips this question and leavesCCC_DEVELOPER_EMAILunset.
Label this machine’s spend with a work item
By default the meter tells you what this machine spent. It can’t tell you which job the money went to. One line in~/.marginfront-ccc/.env fixes that:
status reads the line back to you, so you can check it without opening the file.
Use the same spelling every time, capital letters included. lowai-677 and LOWAI-677 are two different jobs here, and they add up separately. The meter doesn’t check the name for you, so type it the way you mean it.
There’s nothing to restart. Change the line, save the file, and the meter picks up the new name on the next usage it sends. No stop, no start. Blank the line out, or delete it, and this machine is metered exactly as it was before.
Older installs keep working. CCC_MATTER_ID was the earlier name for this line, and a machine still on it reports exactly as it does today. If both lines carry a value, CCC_WORK_ID is the one used. To move a machine to the newer name, re-run npx @marginfront/code-cost-clarity@latest init.
One label per machine. Every record this machine sends carries the same work item until you change the line. If one machine works several jobs at once, this label can’t separate them; tag at record time with metadata.matterId instead (see Matters).
Count what you finish
Counting spend needs nothing from you. This is the other half: what your team finished, so you can see what one of those cost in AI. Two commands:work says what you’re working on now. done says you finished one of the things you named. Add a number to count several at once: done "paper reviewed" 3.
In MarginFront, open ROI in the left menu, then the Finished work tab, to see each one with its AI cost beside it. A finished thing is priced at zero, so recording one never moves your spend number. What shows up beside it is the AI cost attributed to it: what the same developer’s AI spent on the same work item across those dates, split across the things they finished.
It’s off until you turn it on. init asks once, at the end, and the answer defaults to no. Say yes and it saves what you finish and adds a short note to your coding assistant’s memory file (~/.claude/CLAUDE.md for Claude Code, ~/.codex/AGENTS.md for Codex) so the assistant records finished work as it goes. uninstall takes that note back out. To change your mind either way, run npx @marginfront/code-cost-clarity done --setup. status tells you which mode this machine is in.
A few things worth knowing:
- Who it lands under is the developer the meter already uses (
CCC_DEVELOPER_EMAIL). With no developer email,donerecords nothing and tells you how to fix it, rather than filing the count under a name your spend never lands under. - Which assistant it rolls up under is the installed one. With both installed and neither seen working yet,
doneasks you to name one with--agent claude-codeor--agent codex. - Offline it waits. It goes into the same queue your turns use and sends the next time the meter runs. On Linux and WSL that’s the next time you run
npx @marginfront/code-cost-clarity run. - Reuse the same name. The page groups by name and ignores capitals, so
Paper reviewedandpaper reviewedare one row.
Cache pricing
Claude Code reports four kinds of tokens (fresh input, output, cache-read, and cache-creation), and Anthropic prices them differently. This tool splits them into their correct typed fields (cacheReadTokens / cacheWriteTokens) so MarginFront can price each at its own cache rate instead of lumping everything into one input number. That token split is accurate by default.
- Default (recommended). Cache tokens go in the
cacheReadTokensandcacheWriteTokensfields; MarginFront prices each at the model’s catalog rate. --fold-cache(emergency fallback only): for a model MarginFront can’t price yet, this rolls the cache tokens into billed input at the fresh-input rate so the cost is never a silent $0. Cache reads cost less than fresh input, so that slice of the bill rounds up. Cache writes cost more than fresh input, so that slice rounds down; on a GPT-5.6-family Codex model the cache-write portion can read noticeably low (see Also capture Codex). Use it only when a model has no cache price; otherwise the default is more accurate.
NEEDS_COST_BACKFILL (a visible gap, never a silent $0), and one click in MarginFront prices it both retroactively and going forward. The raw cache numbers are also kept in metadata, and Claude Code’s own cache-accurate cost is in metadata.claudeCodeCostUsd to reconcile against.
Which cache-write rate you pay (one setup question)
Anthropic bills cache writes at two different rates, and which one you pay depends on how Claude Code signs in on the machine:- Claude subscription (Pro / Max / Team): 1-hour cache, billed at 2x input
- API key / Bedrock / Vertex: 5-minute cache, billed at 1.25x input
init figures this out for you. If the machine already sets FORCE_PROMPT_CACHING_5M or ENABLE_PROMPT_CACHING_1H (in the shell or in ~/.claude/settings.json’s env block), the answer is detected and saved with no question. Otherwise init asks one question and saves the answer as CCC_CACHE_WRITE_TTL in ~/.marginfront-ccc/.env. Every usage record then carries the answer in metadata.cacheWriteTtl, and MarginFront prices that record’s cache writes at the matching rate.
If it’s unset (you skipped the question, answered “not sure”, or installed with --no-prompt): MarginFront prices cache writes at the 1-hour ceiling (2x input). It can overstate, but it can never under-count. Edit CCC_CACHE_WRITE_TTL (values 5m or 1h) anytime if the machine’s sign-in changes.
A long-context model id likeclaude-opus-4-8[1m]is normalized toclaude-opus-4.8to match MarginFront’s pricing table. The raw id is kept inmetadata.rawModel.
Billable tool calls
Tokens are most of what a coding agent costs, but they aren’t all of it. A paid web search or a metered MCP tool costs money every time it runs, so this tool sends tool calls as their own line items, separate from the token turns. There’s nothing to set up, and nothing to keep in a list. Every tool your agents run is forwarded, and your MarginFront pricing catalog decides what each one is worth:- A tool with a price row is priced at that rate, times the number of calls.
- A tool with no price row lands
NEEDS_COST_BACKFILL, the same visible gap an unpriced model gets. You can price it later, and it’s never a silent charge. - Free built-ins like file reads, shell, and grep are forwarded too, and the catalog prices them $0. Your usage view shows everything the agent did without inventing a cost for it.
mcp__server__tool, so a paid MCP tool is priced apart from a free one sitting next to it. The arguments the tool was called with never leave your machine. See Privacy.
Repeated calls are counted, not listed one by one. If the same tool runs five times inside one reporting window, you get one line item with a quantity of five.
A tool line item carries a count, never tokens. The tokens the model spent deciding to make the call are already on the turn record, so nothing is billed twice.This works the same on macOS and Windows. The one difference is what gets seen in the first place: on Windows the desktop apps aren’t metered yet, so tool calls made there don’t reach the meter.
Also capture Codex
The same tool captures Codex too, and there’s nothing extra to install: Codex reports to the same local collector. You get three setups for free: Claude Code only, Codex only, or both (the same developer’s email shows up across all of it). Turn it on. Add an[otel] block to your ~/.codex/config.toml, pointing at the same collector:
exporter is a table, with the endpoint nested inside it and ending in /v1/logs. (A flat exporter = "otlp-http" with a bare top-level endpoint silently exports nothing. Verified.)
Then run Codex normally. run already watches both sources from the one collector file, so there’s no extra flag. init prints this same block at the end, so you have it handy.
Why Codex’s numbers are counted differently
Anthropic and OpenAI count tokens differently, and getting this wrong silently mis-charges you. For Codex, some of the token counts are already inside the others:- The cached tokens are already part of the input count.
- The cache-write tokens are already part of the input count too.
- The reasoning tokens are already part of the output count.
Not every setup reports a cache-write count. You need a recent Codex build and a model in the GPT-5.6 family. Older Codex builds leave the field out, and models before GPT-5.6 report it as zero. Both read as no cache write.
cacheWriteTokens is sent only when the count is above zero, so those setups send no cache-write field at all and never get flagged for missing a rate they don’t use.
The raw reasoning, tool, cached, and cache-write counts are all still recorded in metadata for your own audit. They’re never billed twice.
--fold-cachecuts both ways on Codex. It bills the whole input count at the plain input rate and sends neither cache field. On the cache-read slice that rounds the bill up, because a read costs less than fresh input. On the cache-write slice it rounds the bill down, because a write costs more than fresh input, about 1.25x. So on a GPT-5.6-family model,--fold-cachecan read roughly 20% low on the write slice. Reach for it only when a model has no cache rates in your catalog yet. The default split is the accurate one.
Whether your email shows up depends on how you sign in
Per-developer attribution rides on the developer’s email from Codex’s telemetry, the same idea as Claude Code. Whether that email surfaces depends on how you sign in to Codex: an org-managed seat or an OpenAI API-key sign-in stamps it; some interactive ChatGPT logins may not. (That sign-in is between you and OpenAI; this tool never sees your OpenAI key.) If it doesn’t surface, the usage is not dropped. It lands under thecodex-no-identity placeholder with a fix hint, exactly like the Claude Code path.
Good to know
- A
gpt-5-codexpricing row must exist first. A full-rate row has to be in MarginFront’s pricing catalog, and on a GPT-5.6-family model that row needs both cache rates, read and write. If a turn reports cache-write tokens and the row has no cache-write rate, that event lands asNEEDS_COST_BACKFILL(a visible gap, never a silent$0). Adding the missing rate is a one-time catalog step. codex-auto-reviewis skipped. It’s an internal pseudo-model, not a real billable model, so the tool drops it rather than mis-price it.codexandcodex execboth export the usage logs this tool reads (verified against a live session). Some Codex versions don’t emit OTel metrics, andcodex mcp-servertelemetry has had bugs, but neither is the logs stream this tool uses, so neither affects capture here.- The
[otel]block above is the verified shape (exporter as a table, endpoint nested with/v1/logs). If a future Codex version changes it, keep the exporter pointed at the local collector on/v1/logs.
One thing to confirm: check the exact Codex dollar figure against one real captured session, both that Codex reports per-turn (not running-total) token counts, and that your developer email shows up under your sign-in mode. The mapping above is the verified-safe default; the confirmation is a quick one-session check, not a reason to wait before installing.
Confirm it landed (independent read-back)
Pull the most recent events for one developer straight from the API: This one is a plain API call, so it runs anywhere, including Windows PowerShell.Maintain / shut off
- Collector version is pinned for safety. The install downloads one known-good collector release and verifies its fingerprint before it ever runs. Moving to a newer collector ships in a package update (so the integrity check always has a fingerprint to verify against). There is no useful per-run version override.
- Stop temporarily:
stoppauses the background meter;start(or re-runninginit) brings it back.statusshows whether it’s running. - Remove it:
uninstallfrees the ~360 MB, stops everything, and reverts the telemetry config it wrote (Claudesettings.json, the Codex[otel]block, and the desktop GUI-session env). It keeps your key;uninstall --purgedeletes your settings too.
Troubleshooting
- “No MARGINFRONT_API_KEY found”: paste your MarginFront key into
~/.marginfront-ccc/.env, orexport MARGINFRONT_API_KEY=...in the shell you startrunfrom. That shell is a macOS terminal, PowerShell on Windows ($env:MARGINFRONT_API_KEY = "..."), or your WSL distro’s prompt.previewworks without a key. - HTTP 401 / 403: wrong or expired key, or you pasted a publishable key (
mf_pk_*) where a secret key (mf_sk_*) is required. If your key stops working while the meter is running, your usage is not thrown away: the meter keeps recording, parks the outgoing turns in an on-disk queue, and re-checks the key every few minutes, sending everything it parked the moment the key works again. Saving a fresh secret key (throughinit, or by editing.envand runningstart) lifts the hold right away. Pull a fresh secret key from your MarginFront dashboard under Build → API keys. - HTTP 422 / validation error: body-shape mismatch. Run
preview <capture.json>and compare the record. - Collector file stays empty: make sure
initfinished and the background meter is running (status), and that you opened a fresh Claude/Codex session afterinit(an already-open app or shell won’t emit until reopened). If it’s still empty, re-runinitto rewrite the collector config: it defaults to http/protobuf/4318, the transport that works with Claude Code, and gRPC/4317 silently exports nothing. usageCostis null: the normalized model id didn’t match the pricing table. Tokens are still recorded; the cache-accurate cost is inmetadata.claudeCodeCostUsd.- Numbers ballooning: the collector’s delta conversion isn’t running. Re-run
initto rewrite the collector config. - Seeing
claude-code-no-identity(orcodex-no-identity)? Your sign-in didn’t surface an email. Use an org-managed Claude seat, or attach a customer mapping. (Codex uses thecodex-no-identityplaceholder when a ChatGPT login doesn’t surface an email.) - Codex spend not showing up? Confirm the
[otel]block is in your user-level~/.codex/config.toml(not a project config), the endpoint ishttp://127.0.0.1:4318, andrunis going. Some Codex versions/subcommands have telemetry bugs, so check your version. - “Collector integrity check FAILED” or “Refusing to run an unverified collector”: the collector that downloaded didn’t match the fingerprint the tool trusts, so it was stopped before it ran. The most common cause is a corporate network that inspects and rewrites downloads (a TLS-inspecting proxy), or a corrupted transfer. Re-run
initon a network without that kind of proxy. Don’t try to bypass the check: a download that keeps failing the fingerprint on a clean network is exactly the case the check exists to stop. The “unverified” wording instead means you pointedCCC_OTELCOL_VERSIONat a release the tool has no fingerprint for. UnsetCCC_OTELCOL_VERSIONto use the built-in pinned version.
Privacy
This tool watches your coding agents, so here’s exactly what leaves your machine and what stays on it.What leaves your machine
Each usage record carries:- The developer’s email, in plain text, on purpose. Per-developer cost has to know who ran the turn, so the email isn’t hashed or scrambled. If your sign-in doesn’t surface one, the usage isn’t dropped. It lands under a clearly labeled placeholder instead (
claude-code-no-identity, orcodex-no-identityfor Codex). - The model name, the token counts, and a session id.
- The name of each tool call. For an MCP tool, that’s the server name and the tool name.
- Your git repo, branch, and commit, when they can be worked out, so you can see which project the spend came from. A repo that lives only on your own disk sends nothing here, because there’s no owner and repo name to send. To work these out, the meter reads folder and file names under Claude Code’s projects directory, and for Codex the first line of the session file, which records the working folder. It never reads past that line.
What never leaves your machine
- What you typed, and what the agent wrote back. Prompts, replies, and session transcripts all stay on your machine.
- Your code and your file contents.
- The arguments a tool was called with. Since 0.16.0 the collector drops them before the local telemetry file is written, along with the shell-command copy in a Bash call’s tool parameters. An MCP tool call keeps only its server and tool names. Only the two names are sent.
- File paths and shell commands.
- Your Anthropic or OpenAI key. This tool never reads it, needs it, or sends it.
Where your MarginFront key lives
Your MarginFront secret key is saved in one file,~/.marginfront-ccc/.env. It’s never written into ~/.claude/settings.json, never into ~/.codex/config.toml, and never into a usage record. The only place it goes is the header on the request to MarginFront, which is how MarginFront knows the usage is yours.
That file is locked to you:
- macOS and Linux: the file is owner-only (mode 600).
- Windows:
initrebuilds the file’s access list so it holds one entry, your own Windows account, and turns off inheritance. Other accounts on the PC can’t open it. If Windows refuses,inittells you and prints the three clicks in File Explorer that fix it. Your key is saved either way, so don’t skip that message.
Reporting turns on for the whole machine
Saying yes at the consent prompt turns usage reporting on for every Claude session on this computer, not only the terminal you set it up in. That’s what lets you start coding without a second terminal, and it’s a real, lasting change, which is whyinit lists everything it touches and asks first.
- In a terminal,
initasks, and you answer. - With
--no-prompt(a server or a script), it goes ahead without asking and still prints the same list. - Piped, with no terminal and no
--no-prompt, it answers no for you and wires nothing up. It does create~/.marginfront-cccand download the collector before it asks, so a declined run leaves that one folder behind and nothing else.
~/.codex/config.tomlis only touched when Codex is on the machine. If you already have your own[otel]settings,initleaves the file alone and prints the block for you to paste.initnever overwrites a telemetry value you’d already set for yourself. It backs the file up before it changes anything.
Turning it all back off
uninstall reverses every change:
- It removes the background meter and the copied meter program.
- It removes only the telemetry settings it added, and only where the value still matches what it wrote, so anything you changed yourself survives.
- It removes only its own
[otel]block from~/.codex/config.toml, and keeps any lines you or Codex put inside it. - It deletes the collector and the runtime files.
--purge. It never edits your ~/.zshrc on the way out: if an older setup left a line in there, uninstall points at it and leaves the decision to you.
Security
- This tool never reads, needs, or transmits your Anthropic or OpenAI API key. The only credential it touches is your MarginFront key, used only to POST usage to MarginFront.
- Your MarginFront API key never lives in the package. It’s saved only in
~/.marginfront-ccc/.env(mode 600) on your machine. The forwarder reads it from the environment only, never hardcoded, never logged. - The published package contains only the built code and its README. Captured telemetry, the collector binary, and your
.envare all kept off the machine that runs this tool and out of the package. - The collector this tool downloads is checked against a built-in SHA-256 fingerprint (pinned from OpenTelemetry’s signed checksums) before it is ever made runnable. If the downloaded file doesn’t match, it is deleted and the install stops, so a download that was tampered with or rewritten in transit never runs on your machine.
For developers (technical appendix)
Input shape: OTLP/JSON,resourceMetrics[].scopeMetrics[].metrics[]. Two metrics matter: claude_code.token.usage (one datapoint per type in input/output/cacheRead/cacheCreation) and claude_code.cost.usage (USD).
Grouping key: (user.email, model, session.id) → one MarginFront record per group.
Token mapping: input→inputTokens, output→outputTokens, cacheRead→cacheReadTokens, cacheCreation→cacheWriteTokens (Anthropic’s cache_creation_input_tokens). With --fold-cache, cache tokens are added into inputTokens instead and the typed fields are omitted (no double count).
Temporality: Claude Code emits cumulative counters. The collector converts them to deltas; the forwarder trusts each line is already an increment.
Ingest: POST https://api.marginfront.com/v1/sdk/usage/record, headers Content-Type: application/json and x-api-key: <key> (not Bearer). Body envelope { records: [...] }. The endpoint auto-creates the customer (by customerExternalId) and agent (by agentCode) on first POST, and resolves your org from the MarginFront API key (the body can’t override it).
Codex source (the second input shape)
Input shape: OTLP/JSON logs,resourceLogs[].scopeLogs[].logRecords[] (a different tree than Claude Code’s resourceMetrics). The usage event is named codex.sse_event; it’s detected by that name (on the event.name attribute or the record body) or, failing that, by the presence of any token-count attribute.
Value encoding: log attribute values arrive as intValue (a JSON string, per the protobuf int64 rule) or doubleValue (a JSON number) for token counts, and stringValue for identity fields. All three are handled.
Token fields: input_token_count, output_token_count, cached_token_count, cache_write_token_count, reasoning_token_count, tool_token_count, plus model, user.email, and a session id (conversation.id / session.id). cache_write_token_count shows up only on recent Codex builds, and models before the GPT-5.6 family report it as zero; both cases read as no cache write. The accurate nested mapping is in the Codex section above. Cached and cache-write are both subtracted from input, and reasoning is never added to output.
Malformed turns: if a turn’s cache slices somehow add up to more than its own input count, the slices are trimmed to fit rather than billed past it, with cache-read keeping priority over cache-write. That record carries metadata.cacheSlicesClamped: true, and its billed cache tokens read lower than the raw counts on the same record. The gap is deliberate, and it errs toward under-billing.
Granularity: one MarginFront record per codex.sse_event (one per turn). There’s no cumulative-to-delta step on the logs pipeline (that’s a metrics-only processor); each Codex log record is already one turn’s usage.
Identity: agentCode: "codex", signalName: "codex-turn", modelProvider: "openai", environment: "development". Records carry metadata.source: "codex".
Routing: both sources write one JSON document per line to the same collector file. The forwarder routes each line by which tree it has (resourceMetrics → Claude Code, resourceLogs → Codex), so one watcher handles either or both.
