Configuration
OpenLimiter keeps everything in one state directory under your own user account: a configuration file, a cache, a lock, and the optional manual document. This page says where that is and what each file does.
The state directory
| platform | path |
|---|---|
| Windows | %LOCALAPPDATA%\openlimiter |
| macOS | ~/Library/Application Support/openlimiter |
| Linux | ${XDG_STATE_HOME:-~/.local/state}/openlimiter |
The directory is created with restrictive permissions where the platform supports them. A path that turns out to be a symbolic link is rejected rather than followed.
What lives in it
| file | role |
|---|---|
openlimiter-config.json | Written by openlimiter init. Records the connector list, whether each one was detected, and the statusline layout. |
openlimiter-cache.json | The one cache every command reads and every writer merges into. |
openlimiter.lock | Held by writers only. Readers never take it. |
manual.json | Optional. Quota you maintain by hand. See ingestion for the shape. |
How the cache behaves
- One schema, one file, one lock. There are no competing state files to reconcile.
- Readers never take the lock. A reader opens the file, validates that open descriptor, and reads through it, so a path swapped after the check cannot redirect the bytes.
- Writers take the lock, and the read, the merge, and the write all happen inside it. A lock older than five seconds is treated as abandoned and reclaimed.
- Every replacement flushes to stable storage before the rename, so a reader observes either the previous content or the new content and never a partial file.
Cache health is visible at any time through openlimiter doctor, which prints the cache status and how many rows were dropped for failing validation.
The statusline layout
The default statusline draws bars for one connected host at a time, in the ten block grammar the agent context page documents. Nine keys in the configuration file decide how a row is built, and openlimiter config is how they are read and changed.
NO_COLOR=1 node packages/cli/dist/bin.js statusline
OpenLimiter NEAR_CAP PREFER ANTIGRAVITY CLAUDE ###.. 64.0% CODEX ####. 84.0% ANTIGRAVITY #.... 28.0% OPENCODE ####. 92.0%
MANUAL #.... 35.0% OPENROUTER ###.. 62.3%Every key
| key | accepts | meaning |
|---|---|---|
order | provider ids, comma separated, or NONE | The order the cells appear in. Whatever you list comes first; every provider you leave out follows the built in order behind it, which is the subscription plans and then the credit and API meters. NONE is the way back to that built in order. Default NONE. |
meters | worst or all | A provider with several meters shows the one closest to its cap, or all of them as separate cells named PROVIDER:METER. Meters order session, daily, weekly, monthly, credits. Default worst. |
width | a whole number from 40 to 400 | The columns one row may spend before the line stacks. Nothing measures your terminal: a statusline host runs the command with no terminal attached, so the number here is the number honoured. Default 140. |
rows | 1 or 2 | How many rows the layout may use. Default 2. |
bars | true or false | False restores the single plain line from 0.1.0, byte for byte. Default true. |
color | auto, always, or never | auto follows the terminal, which for a statusline host usually means no colour, because the host captures the output rather than handing the command a terminal. always is how you say the host understands escape codes. Default auto. |
style | bar or cells | bar is the ten block grammar openlimiter terminal wires by default. cells restores the previous reason led row with a five block bar per provider. Default bar. |
show | provider ids, comma separated, or NONE | Which connected providers actually draw a bar in bar style. NONE, the default, shows every connected provider. openlimiter terminal show and terminal hide are the commands that change it. |
hosts | written by openlimiter terminal, not by hand | Which command each wired host was left running before OpenLimiter took the slot, kept so terminal uninstall can restore it byte for byte. |
Reading and writing them
openlimiter config get statusline
statusline.order=NONE
statusline.meters=worst
statusline.width=140
statusline.rows=2
statusline.bars=true
statusline.color=autoOne key at a time, validated before it is written. A value the key cannot use exits 2 and names what it wanted, and nothing partial is written, because the whole document is replaced atomically. Statusline keys are the only thing this command mutates: the connector list belongs to openlimiter init, and any other key exits 2.
openlimiter config set statusline.order codex,claude
statusline.order=codex,claude
openlimiter config set statusline.meters all
statusline.meters=all
openlimiter config set statusline.width 12
openlimiter config: statusline.width must be a whole number from 40 to 400.
openlimiter config set statusline.theme dark
openlimiter config: unknown statusline key. Known keys: order, meters, width, rows, bars, color.Re running openlimiter init keeps whatever is configured here and only redetects the connectors. A statusline key this version cannot read falls back to its default rather than stopping the draw, so a hand edited typo in one field never costs you the other five.
When the row runs out of room
The line stacks instead of truncating. A break lands between two cells and never inside one, and two rows is the ceiling. Past that the providers closest to their caps are the ones kept, and the last row ends by saying how many it could not show.
openlimiter config set statusline.rows 1 && NO_COLOR=1 node packages/cli/dist/bin.js statusline
OpenLimiter NEAR_CAP PREFER ANTIGRAVITY CODEX ####. 84.0% OPENCODE ####. 92.0% +4 moreThe 0.1.0 line, on request
The format before this layout was one plain line with no bars. If you wrote a script against it, bars false returns it exactly, and a test pins that exact string so it cannot drift.
openlimiter config set statusline.bars false && node packages/cli/dist/bin.js statusline
OpenLimiter NEAR_CAP CLAUDE 64.0% OPENROUTER 62.3% CODEX 84.0% ANTIGRAVITY 28.0% OPENCODE 92.0% MANUAL 35.0% PREFER ANTIGRAVITYClaude Code settings
Use the absolute path to your clone. Forward slashes work on every platform, including Windows.
{
"statusLine": {
"type": "command",
"command": "node /absolute/path/to/openlimiter/packages/cli/dist/bin.js statusline"
},
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "node /absolute/path/to/openlimiter/packages/cli/dist/bin.js hook"
}
]
}
]
}
}Which of the two writes
Only the statusline. It is the path that receives the session payload, so it is the path that updates the cache. The hook reads and never writes.
Credentials
The credential library call sits behind an interface, and the adapter is stubbed in this release, so openlimiter init cannot store a key until a driver is supplied. Nothing else on your machine is touched.
Connector detection
Detection is a pure function of the environment the CLI hands a connector. Facts only the CLI can observe, such as a manual document sitting in the state directory, arrive as explicit markers, which is why openlimiter doctor never claims a connector is ready when it could not receive data.
openlimiter doctor
CONNECTOR DETECTED FRESHNESS DRIFT
claude no unknown UNVERIFIED
openrouter no unknown UNVERIFIED
codex no unknown UNVERIFIED
antigravity no unknown UNVERIFIED
opencode no unknown UNVERIFIED
manual yes fresh UNVERIFIED
CACHE ok DROPPED 0A cache that would not parse, or one that parsed with rows thrown out, adds one more line in red saying so. It reports against the cache rather than against a provider, because a corrupt file cannot be trusted to say whose reading it held.