# Usage Dashboard

This dashboard aggregates usage for Claude, Codex, and Gemini with a local cache-first strategy to reduce remote polling pressure.

## Data sources

- Claude: Anthropic OAuth usage endpoint for quota windows.
- Codex: ChatGPT backend usage endpoint for plan and rolling-window usage.
- Gemini: local `~/.gemini` session history for per-model token totals, with Google quota metadata used only to enrich reset timing when available.

## Cache behavior

- Cache file: `/cache/usage.json`
- Default TTL: `1800` seconds (`30` minutes)
- `/api/usage` returns cached provider data when the last refresh is newer than the TTL.
- Once the cache is older than the TTL, the backend refreshes that provider and rewrites `usage.json`.

## Claude 429 handling

- Claude keeps a provider-level backoff state in `usage.json`.
- If Anthropic returns `429`, the dashboard stores `backoffUntil` and stops retrying until that deadline.
- When a previous successful Claude payload exists, the API serves it back as stale data during the backoff window.
- When no prior Claude payload exists, the API still records the backoff window and returns a rate-limited error without repeatedly re-querying Anthropic.

## Gemini model usage

- Gemini model usage is read from local session files under the mounted Gemini home directory.
- The UI shows per-model token totals, message counts, and last activity time.
- Quota reset timing is shown only when quota metadata is available for the same model.

## Environment

- `USAGE_CACHE_FILE`: cache file path. Default: `/cache/usage.json`
- `USAGE_CACHE_TTL_SECONDS`: cache TTL in seconds. Default: `1800`
- `CLAUDE_CREDS`, `CODEX_CREDS`, `GEMINI_CREDS`: credential file paths
- `GEMINI_ROOT_DIR`: Gemini home directory used for session scanning

## Verification

The updated `/api/usage` flow was validated with a temporary container run against mounted local credentials and cache storage:

- authenticated `GET /api/usage` returned `200`
- Gemini returned token-based model windows from local session history
- Claude `429` responses wrote backoff state into `usage.json`
- a second immediate request reused the stored Claude backoff instead of issuing another live retry
