Limits
How much of each Claude Code and Codex subscription is left, asked of the products themselves — and the day the poller signed me out.
Every Claude Code and Codex subscription has windows: five hours and seven days for Claude, a five-hour and a weekly window per model family for Codex. The products show you where you stand inside them, one login at a time, on a screen you have to be in. Across three logins on two machines that is a lot of screens, and none of them tell you the one thing worth knowing at a glance: how long until the window you are about to hit resets.
ovm limits is that glance. It polls each account, writes one file, and draws one screen.
1Through the product, not around it
Anthropic does not publish usage windows anywhere a third party may read them. Claude Code does hand them to its own statusline script, in the interactive TUI, after the first assistant reply of a session. So a poll is exactly that: a throwaway session in an empty directory, one word typed on the cheapest model, the statusline payload that follows carries the windows, then /exit. The credential never leaves the unmodified Claude Code binary. Each poll is one real turn, about 37k input tokens of system prompt against a 39-token reply, which the statusline prices at two cents and the subscription prices at nothing.
Codex is easier. codex app-server speaks the JSON-RPC the IDE extension uses, and account/rateLimits/read returns every window with its reset time, free.
Most of the menu-bar apps that do this job take a shorter road: read the OAuth token out of the Keychain and call an undocumented usage endpoint. It is faster and it costs no turn. It also means a tool that promises to watch your usage is holding your credential, and depends on an endpoint nobody committed to keeping. This one does neither.
2The day it signed me out
The first cut polled the login you work in. On 6 September every open Claude Code session on the laptop went to the sign-in screen at once. A poll is a real session, a real session refreshes its OAuth token, and the refresh rotates the grant; the sessions holding the old one were out.
So an account is now a home of its own under ~/.ovm/limits/homes/, signed in once, and a poll aimed at ~/.claude or ~/.codex is refused outright. That decision shaped the rest. Accounts stopped being “whatever claude is signed into here” and became a registry: claude-1, claude-2, codex-1, each with a label you can change, each registered on purpose. The id names the home, and the product keys its stored login to that path, so renaming costs nothing and renumbering is not a plain mv. ovm limits with no arguments is the registry on screen, drawn with the same primitives as ovm switch, which moved into a crate of their own for the purpose.
3Surprise resets
Inside a window usage only climbs. Every turn adds to it, including the poll's own. So when a poll finds usage lower than the poll before, and the window's reset time was still ahead, the provider cleared it early. That is the event worth telling someone about; the scheduled rollover is the clock.
A poll now diffs against the last one. Surprise resets, scheduled rollovers, thresholds crossed, a poll that starts failing or recovers, all go to a log, and hooks run on them. hook ntfy <topic> is one line and puts a surprise reset or a dead login on your phone. Beside limits.json, which carries everything both products report, the poller writes resets.json, which carries only the surprise resets and when it will next look: no percentages, no accounts, no host. A home Mac uploads that after every poll and ovm.sh/limits counts down from it.
4Two things a poller has to get right
A Claude poll could hang forever on the way out. Told to leave, the session restores its screen, and a process cannot finish closing its terminal until that output has been read. The poller had stopped reading by then and was blocked in wait on a child stuck in exit. One had been sitting like that for 42 minutes when I found it. Every wait now drains the terminal and is bounded, and the parent drops its own copies of the terminal right after the fork. The regression test floods a terminal on SIGTERM and fails on the clock if finish hangs.
And a background poller that stops is worse than none, because it looks like quiet. The mini already had a usage watcher, written in June, dead since June, logs empty, nobody told. The mini already pushes a heartbeat every five minutes that a hosted dead-man's switch reads; the next step is for the poll's age to ride in it, so the page that fires when the mini goes dark fires when the poll goes stale too.
5What it costs
At the laptop's hourly cadence, one Haiku turn per Claude account per hour, plus one whenever a window has just reset. The mini polls every fifteen minutes to catch resets within a quarter hour, which is about 96 tiny turns a day per account, a fraction of a percent of the window it is measuring. Codex is free at any cadence.
curl -fsSL https://ovm.sh/install | sh ovm limits # register, sign in, poll ovm limits hook ntfy <topic> # a surprise reset on your phone ovm limits agent install --every 15m