A small, always-on-top desktop window that shows how much of your Claude and Codex subscription quota you have left — read locally, from your own credentials, with no account to create and nothing phoned home.
The entire value proposition is a small, auditable trust boundary. Credentials and the network are owned by two modules — crates/usage-core/src/credentials/ and crates/usage-core/src/egress/ — deliberately small enough to read end to end in one sitting; the two provider parsers consume what they return. Everything else is scheduling and rendering.
- Claude (Anthropic) — your 5-hour and 7-day subscription windows: percent used, and how long until each resets.
- Codex (OpenAI) — the rate-limit windows the Codex endpoint reports, labelled by their duration (typically a short rolling window plus a weekly one).
- Per-model breakdown — where a provider reports per-model limits, each provider pane has a collapsible toggle that expands them into their own rows.
- Pace — elapsed-time markers on every bar, and a burn-rate forecast that speaks up only when the current spend rate would exhaust a window before it resets.
- Sparklines (opt-in) — with
history=on, a quiet 24-hour strip under each provider's bars: the day's shape at a glance. - Alerts (opt-in) — with
alerts=on, a banner, a red ring on the tray icon, and one taskbar attention request when a window crosses your line. Time-aware by default: a healthy 85% late in the week stays quiet. - Freshness — a dot on each provider header ages green → amber → red as the data does; the exact seconds are on hover. You are never quietly shown a stale number.
- System tray — an icon rendering current usage, with a tooltip and a Show/Hide/Quit menu (Windows and macOS; see Platform support).
- Headless mode —
quotapane-cliprints the same normalized snapshot as text or JSON, for scripts, for cron, and for proving to yourself what the app talks to. (Text output is a compact summary; per-model rows and reset credits appear in--jsonand the window.) - A gate for scripted runs —
--fail-at <N>exits non-zero when a quota window reaches N percent, and--watch <SECS>polls on an interval, so a long agentic or batch run can stop before it dies mid-flight. QuotaPane runs no commands of its own: it reports, your script decides. - An agents view —
usage // agentsin the titlebar switches the pane to the Claude Code and Codex CLI sessions running on this machine (see below).
Two binaries are produced: quotapane (the window) and quotapane-cli (headless).
The window ships with the Cipher Pine terminal theme. A tray-menu
item switches between it and a plain look, live; the choice is
remembered in config.cfg under your platform's config directory
(%APPDATA%\quotapane\ on Windows, ~/.config/quotapane/ on
Linux). No tray on your platform? Launch with --plain or
--themed to pick per run.
config.cfg is one key=value per line — # comments and blank
lines are ignored, unknown keys are ignored, and anything unparsable
falls back to the default shown here. Deleting the file restores
every default.
| Key | Values | Default | Meaning |
|---|---|---|---|
theme |
cipherpine | plain |
cipherpine |
Which look the window wears. |
history |
on | off |
off |
Append usage percentages to history.jsonl (next to this file) and draw a 24 h sparkline under each provider's bars. Timestamps, window labels and percentages only — never credentials. |
alerts |
on | off |
off |
Raise a quota alert: an in-window banner, a red ring on the tray icon, and one taskbar attention request. |
alert_at |
1–100 |
80 |
Percent of a window at which an alert becomes a candidate. |
alert_mode |
pace | threshold |
pace |
pace only alerts when the window is also being spent faster than it is elapsing; threshold alerts on every crossing. |
Pre-1.6 installs stored the theme as a single word in theme.cfg.
That file is still read when config.cfg is absent, so your theme
carries over; it is never written again, and never deleted.
--pace-demo renders a fixed made-up scenario so the pace markers can
be seen without waiting hours for real usage to produce one: it shows
fake data, polls nothing, reads no credentials, and talks to no host.
The titlebar carries a switcher: usage // agents. Click agents and
the pane lists the Claude Code and Codex CLI sessions running on this
machine — a state dot (green working, amber idle, faint finished),
project · branch · id8, and how long since each last wrote. A row
marked · sub is a subagent. The pane opens on the last two hours,
with anything older one click away behind a // N older today line, so
a morning's finished sessions do not crowd out the one running now. A
session that is still going carries a second line: a ten-minute activity
strip, how long it has been up, the CLI version, and — for Claude Code,
whose transcript says so — whether it is in the loop or it is your turn.
Identity only, never content. The list comes from the session-log
files those CLIs already write (~/.claude/projects/,
~/.codex/sessions/), opened read-only, and QuotaPane extracts a fixed
allowlist of metadata keys from them: ids, timestamps, record types, the
working directory, the git branch, and the CLI's own version string. The
second line above is made of the same stuff — the activity strip counts
timestamps, and the turn phrase reads a record's type and stops. Your
conversations are never
deserialized, never rendered, never stored, and never sent anywhere —
that is SECURITY.md invariant 8, and a test plants
sentinel text in a fixture transcript and asserts it cannot reach any
output. Liveness is inferred from file modification times, so a log
QuotaPane cannot parse still reports honestly instead of disappearing.
The scan runs only while the agents view is showing — on switch, then
every two seconds. Leave the window on usage and no session log is
read at all. Nothing is written, and nothing about these sessions leaves
your machine. --agents-demo opens the view on a synthetic session list,
touching no real log, for anyone who wants to see the feature before
pointing it at their own work.
- Tokens are never persisted, never logged, never serialized. They live in memory in a
Secret<T>that zeroizes on drop and prints«redacted». - Network egress is deny-by-default through a single chokepoint with a compile-time allowlist of exactly two hosts (
api.anthropic.com,chatgpt.com). Anything else is a hard error, and tests prove it. - No first-party telemetry, of any kind, to anyone. CI enforces its absence on every push.
- No auto-update and no update check — there is no updater in the codebase at all. Updating is always something you do deliberately.
- Credential files are opened read-only. Token refresh is delegated to the official
claude/codexCLIs; QuotaPane never writes them. - Proxy support is opt-in and fails closed: with a proxy variable set and no opt-in, nothing is sent. Opting in is a per-run CLI flag behind an explicit warning that a TLS-inspecting proxy can observe your bearer token; the window has no opt-in at all.
Every claim above is a numbered invariant in SECURITY.md, and the mapping from each invariant to the live tests that prove it is machine-checked in CI on every push (invariants.manifest + tools/check-invariants.py, a required check). These are not promises in prose — the docs cannot silently drift from the code.
Full detail: SECURITY.md · THREAT_MODEL.md · ARCHITECTURE.md.
Download the archive for your platform from GitHub Releases, then verify it (below) before running it.
| Platform | Artifact |
|---|---|
| Windows (x86-64) | quotapane-v<version>-x86_64-pc-windows-msvc.zip |
| Linux (x86-64, glibc) | quotapane-v<version>-x86_64-unknown-linux-gnu.tar.gz |
| macOS | Build from source — see below. |
Each archive contains both binaries, both licenses, this README, and a TOOLCHAIN.txt recording the exact rustc / cargo that built it. There is no installer and nothing to uninstall: the binaries are self-contained. QuotaPane writes at most two files, both under your config directory: config.cfg (your preferences) and, only if you turn history=on, history.jsonl (timestamps, window labels and percentages — see Theming and preferences). Credentials are never written.
QuotaPane reads credentials your provider CLI already wrote, so sign in with claude and/or codex first.
Release artifacts are built only by .github/workflows/release.yml on a version tag, never on a maintainer's machine. Every release ships SHA256SUMS covering all archives, a cosign keyless signature over that file as a Sigstore bundle (SHA256SUMS.sigstore.json, which carries both the signature and the signing certificate), and a build-provenance attestation on each archive. Verifying all three takes about a minute.
1. Checksum. Put the archive next to SHA256SUMS, then:
sha256sum --ignore-missing -c SHA256SUMSOn Windows PowerShell, compare manually against the matching line in SHA256SUMS:
Get-FileHash quotapane-v<version>-x86_64-pc-windows-msvc.zip -Algorithm SHA2562. Signature. SHA256SUMS is signed with cosign keyless signing, so the identity is this repo's release workflow rather than a long-lived key:
cosign verify-blob SHA256SUMS \
--bundle SHA256SUMS.sigstore.json \
--certificate-identity-regexp '^https://github\.com/cipherpine/quotapane/\.github/workflows/release\.yml@refs/tags/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comThis prints Verified OK. The identity regex is deliberately narrow: it matches only this repository's release.yml running on a tag ref, so a signature produced by any other workflow, or by this one on a branch, fails.
You need a cosign that understands Sigstore bundles — confirmed with cosign 3.1.2 against a bundle produced by cosign 3.0.6 in CI. Releases no longer ship the detached SHA256SUMS.sig / SHA256SUMS.pem pair that cosign 2.x used.
On Windows, run this from PowerShell or WSL rather than Git Bash. Git Bash rewrites the \. escapes in the identity regex before cosign sees them and the match fails; if you must use Git Bash, write [.] in place of each \..
3. Provenance. Each archive carries a GitHub build-provenance attestation, verifiable with the gh CLI:
gh attestation verify quotapane-v<version>-x86_64-pc-windows-msvc.zip --repo cipherpine/quotapaneA passing signature proves the artifact came from this repository's CI at a specific commit. It does not prove that commit was benign — see residual risk R2 in THREAT_MODEL.md. For maximum assurance, build from source.
cargo build --release --locked
cargo test --workspace --locked # includes the security invariant testsRequires Rust 1.92+ (the workspace sets rust-version = "1.92"; the floor comes from eframe 0.35). Binaries land in target/release/ as quotapane and quotapane-cli.
The window takes no arguments in normal use. Drag it to position it; drag the grip at the bottom edge to choose its height, or double-click the grip to snap the window to exactly fit its content. The height is remembered.
quotapane [--client-version <VER>] [--codex-user-agent <UA>] [--no-tray]
| Flag | Meaning |
|---|---|
--client-version <VER> |
The claude-code version string sent as the Claude User-Agent. Defaults to 0.0.0, which the endpoint throttles aggressively — pass a real version for normal use. |
--codex-user-agent <UA> |
Override the User-Agent sent to the Codex endpoint. Defaults to the Codex CLI's own. |
--no-tray |
Start without the system tray icon. The escape hatch if tray creation fails. |
The headless CLI takes exactly one mode: --once or --watch <SECS>.
quotapane-cli (--once | --watch <SECS>) [--json]
[--provider claude|codex|all] [--fail-at <N>]
[--client-version <VER>] [--debug-raw] [--debug-raw-unsafe]
| Flag | Meaning |
|---|---|
--once |
Poll once and exit. Exactly one of --once and --watch is required. |
--watch <SECS> |
Poll every SECS seconds until interrupted. SECS must be at least 180 — the same polling floor the window respects, applied to scripted polling too. Text output precedes each cycle with a --- <RFC 3339 UTC timestamp> --- separator; with --json, each cycle is one compact line (NDJSON). |
--fail-at <N> |
Exit 3 if any window is at or over N percent used (N is 1–100), after printing the normal output. Checked over every window of every provider that polled successfully — headline and per-model, because a gate should fail safe. Under --watch, the first tripping cycle exits. |
--json |
Emit the normalized snapshot as JSON instead of a text summary. With --provider all, emits an array. The keys are documented in docs/cli-json.md, which also states the stability policy. |
--provider <WHICH> |
claude, codex, or all. Default: claude. |
--client-version <VER> |
As above. |
--debug-raw |
Print the provider's wire response instead of a snapshot, for pinning an undocumented endpoint's schema. Takes precedence over --json. Redacted by default: the value of every email, user_id, account_id, and id key is replaced with «redacted» at any nesting depth, and a body that is not valid JSON is withheld rather than dumped. |
--debug-raw-unsafe |
The same dump, byte-exact — no redaction, no withholding — after a stderr warning. The output can contain your email address and account identifiers, so treat it as private. Implies --debug-raw. |
--allow-proxy |
Send this run through the proxy in your environment. Off by default, and the default fails closed: while HTTPS_PROXY/HTTP_PROXY/ALL_PROXY (either casing) is set and this flag is absent, QuotaPane sends nothing and exits with an error naming the variable — it does not connect directly instead. A TLS-inspecting proxy can read your bearer token, so opting in is explicit and lasts one run. The window has no equivalent flag. |
-h, --help / --version |
Print help or version and exit. |
Exit codes — what a script branches on:
| Code | Meaning |
|---|---|
0 |
Success; with --fail-at, all windows under the threshold. |
1 |
A provider or credential error. |
2 |
Usage error. |
3 |
--fail-at tripped: a window reached the threshold. |
So the gate in front of a long run is one line:
quotapane-cli --once --provider all --fail-at 85 || exit 1QuotaPane never executes anything on your behalf — --fail-at reports, and your script decides.
If a token has expired, QuotaPane says so and tells you to run claude or codex — it never refreshes tokens itself.
Windows is the primary target. macOS and Linux are built and tested in CI on a best-effort basis.
The system tray is Windows and macOS only. On Linux, QuotaPane is window-only: the tray backend would require the officially-unmaintained gtk-rs 0.18 + libappindicator chain, which is not a dependency this project is willing to put in its tree. See the tray-icon row in CONTRIBUTING.md for the full analysis.
v1.0 shipped July 2026: both subscription providers, the always-on-top window, the system tray, the per-model breakdown, and the headless CLI — packaged as signed, attested releases. Since then: per-model truth from the endpoint's own limits array (v1.1), the Cipher Pine theme and the live tray miniature (v1.2), pace markers and forecast-to-limit (v1.3), a full adversarial security review and its remediations (v1.4), CLI automation — --fail-at and --watch (v1.5), opt-in history, sparklines and time-aware alerts (v1.6), and the resizable window plus the agents view (v1.7).
M4 (opt-in official billing APIs) was withdrawn on security grounds (ADR-002, in ARCHITECTURE.md). Both vendors' usage/cost endpoints require an organization admin API key, are unavailable to individual subscribers, measure a different thing than subscription quota, and would force this trust boundary to hold the highest-blast-radius secret in either ecosystem — the exact opposite of the point of this project.
Next: package-manager distribution (WinGet / Homebrew / AUR), and a statusline output mode so the CLI can feed Claude Code's own status bar. Under consideration, on exactly the terms SECURITY.md invariant 5 pre-commits to: an update check that would be notify-only and off by default — today there is none of any kind. Still deferred: the token-free OtelSource (the only acceptable route to any cost view).
QuotaPane has no login of its own — it reads the credential files the
official claude / codex CLIs keep on your machine, and it never
writes them. When the stored token's lifetime runs out, QuotaPane fails
closed: it stops sending the stale token and shows the message instead.
The refresh happens in the provider's CLI, not in QuotaPane:
- Claude — start any
claudesession (evenclaude -p hi). The CLI refreshes its token file as it starts working. - Codex — run
codex login.
That's all. QuotaPane rechecks every 3 minutes while a token is expired and recovers on its own — no restart, no clicks.
QuotaPane is an independent, community project — not affiliated with, endorsed, or supported by Anthropic or OpenAI. The subscription/quota view relies on undocumented endpoints that may change or break at any time, uses your own local credentials only, bypasses no authentication, and scrapes nothing. To read subscription usage, QuotaPane sends the same User-Agent: claude-code/<version> header the official Claude Code client uses — the endpoint rate-limits requests without it — so these requests present as the official client; the Codex provider likewise sends the Codex CLI's default User-Agent (codex-cli). Each provider queries only the endpoint its official client already calls, with your own token, read-only. Use at your own risk.
See CONTRIBUTING.md. Security issues go through GitHub private vulnerability reporting, not a public issue — see SECURITY.md.
Dual-licensed under MIT or Apache-2.0, at your option. Contributions are accepted under the same terms.




