Last month I realized I was opening 6 browser tabs every morning just to check my AI API quotas. Anthropic, Copilot, Codex, three more. Each with different billing cycles, different UI, different definitions of "usage."
So I built onWatch — a CLI that polls all of them from one terminal.
The Problem
I use Claude Code, Antigravity, and Codex CLI daily. Each has its own dashboard:
- Anthropic tracks 5-hour limits, weekly all-model limits, and weekly Sonnet limits — three separate quotas with different reset windows
- Codex has 5-hour limits plus review request quotas
- Antigravity splits quotas across Claude+GPT, Gemini Pro, and Gemini Flash
That's 6 provider dashboards, each with multiple quota types, different billing cycles, and no cross-provider view.
The real problem isn't seeing current usage — every provider shows that. The problem is having no historical data. Which sessions burn quota fastest? Which days am I heaviest? When exactly does my cycle reset? No provider dashboard answers these questions.
The breaking point: I hit a rate limit mid-session with Claude Code. Lost my context, lost my momentum, and had no idea when the limit would reset.
What I Built
onWatch is a Go CLI that runs as a background daemon. It:
-
Polls 6 providers in parallel — each provider runs as an independent agent with its own goroutine (
internal/agent/). Anthropic, Codex, Copilot, Antigravity, Synthetic, and Z.ai. -
Stores history in SQLite — a single
*sql.DBconnection with WAL mode. Currently my database is 55MB after weeks of continuous polling across all 6 providers. -
Serves a Material Design 3 dashboard — HTML templates and static assets embedded via Go's
embed.FS, served from the same binary. Dark and light mode.
The key insight that drives everything: per-cycle, historical usage patterns are more valuable than point-in-time snapshots.
The dashboard shows things like:
- Your 5-hour Anthropic limit is at 72% utilization with 3h 9m until reset
- Your burn rate is 38.2%/hr — at this pace, quota exhausts in 43 minutes
- Your average 5-hour limit usage across 21 windows is 60%
- Recent usage is trending -16% compared to earlier periods
These are real numbers from my running instance, not mock data.
Technical Decisions
Single binary (~13MB): Everything — HTML templates, JavaScript, CSS, service worker manifest — is embedded using Go's embed.FS. No npm build step, no external files. You download one file and run it.
//go:embed templates/*.html
var templatesFS embed.FS
//go:embed all:static/*
var staticFS embed.FS
<50MB RAM with 6 providers: My running instance uses ~35MB RSS with all 6 providers polling every 60 seconds. Key decisions:
- Single SQLite connection (not a pool) —
*sql.DBwith one connection - Bounded queries: cycles capped at 200, insights at 50
- No ORM — parameterized SQL everywhere for both performance and injection prevention
- Embedded assets mean zero runtime allocations for serving static files
The agent pattern: Each provider is an independent Agent struct that runs a polling loop in its own goroutine. Clean shutdown via context.Context cancellation. If one provider's API is slow or errors, others aren't affected.
type Agent struct {
client *api.Client
store *store.Store
tracker *tracker.Tracker
interval time.Duration
logger *slog.Logger
sm *SessionManager
}
func (a *Agent) Run(ctx context.Context) error {
// Polls immediately, then at configured interval
// until context is cancelled
}
Security:
-
subtle.ConstantTimeComparefor credential checks (timing attack prevention) - Parameterized SQL only — plus an
validateOrderByColumnallowlist function for ORDER BY clauses - All data stays on your machine — zero telemetry, no cloud dependency
The Numbers
These are from the actual codebase and running instance:
| Metric | Value |
|---|---|
| Go source lines | ~47,600 |
| Test functions | 486 across 33 test files |
| Binary size | 13MB |
| Runtime memory | ~35MB RSS (6 providers polling) |
| Database after weeks | 55MB |
| Provider agents | 6 (each with client, store, tracker, agent layers) |
| Static assets | 6 files embedded (app.js, style.css, sw.js, manifest.json, favicon.svg, theme-init.js) |
Try It
One-line install (macOS/Linux):
curl -fsSL https://raw.githubusercontent.com/onllm-dev/onwatch/main/install.sh | bash
Windows (PowerShell):
irm https://raw.githubusercontent.com/onllm-dev/onwatch/main/install.ps1 | iex
Docker:
cp .env.docker.example .env # add your API keys
docker-compose up -d
It self-daemonizes on macOS, uses systemd on Linux, and runs in the foreground in Docker (auto-detected via IsDockerEnvironment()).
Open-source, GPL-3.0: github.com/onllm-dev/onwatch
What provider should we add next? MiniMax and Kimi are the most requested.

Top comments (0)