Skip to content
 
 

Latest commit

 

History

461 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

onWatch

Free, open-source AI API quota monitoring for developers.

Track usage across Synthetic, Z.ai, Anthropic, Codex, GitHub Copilot, MiniMax, Gemini CLI, Cursor, and Antigravity in one place. See history, get alerts, and open a local web dashboard before you hit throttling or run over budget. Additionally, you can ingest local telemetry from your own API-driven workflows with API Integrations, keeping track of token use and spending across multiple providers.

This fork is tuned for my local Windows setup and server1 homelab deployment. See Personal Fork Changes for the current fork-specific behavior, UI changes, deployment notes, and remaining TODOs.

onWatch fills the gap between "current usage snapshot" and the historical, per-cycle, cross-session view that developers actually need. It runs as a lightweight background agent (<50 MB RAM with all nine providers polling in parallel), stores historical data in SQLite, and serves a Material Design 3 web dashboard with dark/light mode.

It works with any tool that uses Synthetic, Z.ai, Anthropic, Codex, GitHub Copilot, MiniMax, Gemini CLI, Cursor, or Antigravity API keys, including Cline, Roo Code, Kilo Code, Claude Code, Codex CLI, Cursor, GitHub Copilot, MiniMax Coding Plan, Antigravity, and others.

Zero telemetry. Single binary. All data stays on your machine.

Beta: onWatch is currently in active development. Features and APIs may change.

Anthropic Dashboard - Light Mode

Quick Start

macOS & Linux

One-line install:

curl -fsSL https://raw.githubusercontent.com/Micsushi/onWatch/main/install.sh | bash

This downloads the binary to ~/.onwatch/, creates a .env config, sets up a systemd service (Linux) or self-daemonizes (macOS), and adds onwatch to your PATH.

On macOS, the installer downloads the standard binary with menubar support.

Linux notes:

  • The menubar/tray companion is macOS-only. On Linux it is a no-op stub; use the web dashboard on port 9211 instead.
  • Optional packages improve auto-detection (core polling works without them):
    • libsecret-tools - secret-tool for reading the GNOME/KDE keyring
    • iproute2 (ss) or net-tools (netstat) - Antigravity local port discovery
    sudo apt install libsecret-tools iproute2   # Debian/Ubuntu

Homebrew (macOS & Linux)

brew install Micsushi/tap/onwatch
onwatch setup    # Interactive setup wizard for API keys and config

Note: Homebrew requires a separate tap repository (Micsushi/homebrew-tap). See the Homebrew tap docs to set one up.

Windows

One-line install (PowerShell):

irm https://raw.githubusercontent.com/Micsushi/onWatch/main/install.ps1 | iex

Or download install.bat from the Releases page and double-click it.

This downloads the binary to %USERPROFILE%\.onwatch\, runs interactive setup for API keys, creates a .env config, and adds onwatch to your PATH.

For manual setup or troubleshooting, see the Windows Setup Guide.

Manual Installation

Download binaries from the Releases page. Binaries are available for macOS (ARM64, AMD64), Linux (AMD64, ARM64), and Windows (AMD64).

Or build from source (requires Go 1.25+):

git clone https://github.com/Micsushi/onWatch.git && cd onWatch
cp .env.example .env    # then add your API keys
./app.sh --build && ./onwatch --debug    # or: make build && ./onwatch --debug

Or use Docker (requires Docker or Docker Compose):

cp .env.docker.example .env   # add your API keys
docker-compose up -d

Linux bind mounts: the container runs as non-root (UID 65532). If you bind-mount a host data directory instead of using the named volume, give it to that UID first, or the container cannot write to it:

mkdir -p onwatch-data && sudo chown -R 65532:65532 onwatch-data

Or via app.sh:

./app.sh --docker --run

The Docker image uses a distroless base (~10-12 MB) and runs as non-root. An Alpine variant with shell access is also available (ghcr.io/micsushi/onwatch:alpine). Data persists via volume mount at /data. Logs go to stdout (docker logs -f onwatch). See Docker Deployment for details.

Configure

Edit ~/.onwatch/.env (or .env in the project directory if built from source):

SYNTHETIC_API_KEY=syn_your_key_here       # https://synthetic.new/settings/api
ZAI_API_KEY=your_zai_key_here             # https://www.z.ai/api-keys
ANTHROPIC_TOKEN=your_token_here           # Auto-detected from Claude Code credentials
ANTHROPIC_TOKEN_ROTATION=off              # Safe default for shared Claude Code credentials
CLAUDE_CONFIG_DIR=/path/to/isolated-profile # Optional isolated Claude credential directory
ANTHROPIC_CLAUDE_PATH=/path/to/claude     # Optional Claude executable override
CODEX_TOKEN=your_token_here               # Recommended for Codex-only setups
COPILOT_TOKEN=ghp_your_token_here         # GitHub PAT with copilot scope (Beta)
ONWATCH_ADMIN_USER=admin
ONWATCH_ADMIN_PASS=devpass

At least one provider key is required. Configure any combination to track them in parallel. Anthropic tokens are auto-detected from Claude Code credentials (macOS Keychain, Linux keyring, or ~/.claude/.credentials.json). For Codex-only setups, set CODEX_TOKEN in .env; during runtime onWatch re-reads Codex auth state from ~/.codex/auth.json (or CODEX_HOME/auth.json) and picks up token changes. Copilot tokens require a GitHub Personal Access Token (classic) with the copilot scope.

Provider setup guides:

Run

onwatch              # start in background (daemonizes, logs to ~/.onwatch/data/.onwatch.log)
onwatch --debug      # foreground mode, logs to stdout
onwatch stop         # stop the running instance
onwatch status       # check if running

Open http://localhost:9211 and log in with your .env credentials.


What onWatch Tracks (That Your Provider Doesn't)

┌──────────────────────────────────────────────────────────────────┐
│ What your provider shows          │ What onWatch adds           │
├───────────────────────────────────┼──────────────────────────────┤
│ Current quota usage               │ Historical usage trends      │
│                                   │ Reset cycle detection        │
│                                   │ Per-cycle consumption stats  │
│                                   │ Usage rate & projections     │
│                                   │ Per-session tracking         │
│                                   │ Multi-provider unified view  │
│                                   │ Live countdown timers        │
└───────────────────────────────────┴──────────────────────────────┘

Dashboard -- Material Design 3 with dark/light mode (auto-detects system preference). Provider tabs appear for each configured provider:

  • Synthetic -- Subscription, Search, and Tool Call quota cards
  • Z.ai -- Tokens, Time, and Tool Call quota cards
  • Anthropic -- Dynamic quota cards (5-Hour, 7-Day, 7-Day Sonnet, Monthly, etc.) with utilization percentages and isolated credential ownership through the official Claude executable.
  • Codex -- Dynamic quota cards (LLMs, Review Requests) with OAuth auth-state refresh, historical cycle analytics, and multi-account support (Beta) for tracking multiple ChatGPT accounts
  • GitHub Copilot (Beta) -- Premium Interactions, Chat, and Completions quota cards with monthly reset tracking
  • MiniMax Coding Plan -- Shared quota pool tracking for M2, M2.1, and M2.5 models with 5-hour rolling window reset cycles and multi-account support for tracking multiple MiniMax subscriptions via the dashboard UI
  • Gemini CLI (Beta) -- Per-model quota tracking for Gemini 2.5/3.x Pro, Flash, and Flash Lite models with 24-hour reset cycles
  • Antigravity -- Multi-model quota cards (Claude, Gemini, GPT) with grouped quota pools, logging history, and cycle overview
  • Cursor -- Individual, Team, and Enterprise account tracking with auto-detected credentials from Cursor Desktop SQLite or macOS Keychain/Linux keyring, OAuth token auto-refresh, burn rate forecasts, and on-demand spend tracking
  • API Integrations -- Local JSONL ingestion for custom API-driven workflows and automations. Track per-integration token volume, request counts, recent activity, costs, trends, and accumulated usage across separate API keys and providers.
  • All -- Side-by-side view of all configured providers
  • Prometheus metrics endpoint (Beta) -- Exposes /metrics for Prometheus/Grafana/Alertmanager integrations, with optional bearer token protection via ONWATCH_METRICS_TOKEN
  • PWA installable -- Install onWatch from your browser for a native app experience (Beta)

Each quota card shows: usage vs. limit with progress bar, live countdown to reset, status badge (healthy/warning/danger/critical), and consumption rate with projected usage.

Time-series chart -- Chart.js area chart showing all quotas as % of limit. Time ranges: 1h, 6h, 24h, 7d, 30d.

API Integrations chart -- Dedicated telemetry view for custom API-driven scripts. Switch between tokens per request, request counts, accumulated token use, and cost where available.

Insights -- Burn rate forecasting, billing-period averages, usage variance, trend detection, and cross-quota ratio analysis (e.g., "1% weekly ~ 24% of 5-hr sprint"). Provider-specific: tokens-per-call efficiency and per-tool breakdowns for Z.ai.

Cycle Overview -- Cross-quota correlation table showing all quota values at peak usage points within each billing period. Helps identify which quotas spike together.

Sessions -- Every agent run creates a session that tracks peak consumption, letting you compare usage across work periods.

Settings -- Dedicated settings page (/settings) with tabs for general preferences, provider controls, notification thresholds, SMTP email configuration, and portable history import/export.

Custom API Integrations setup -- Use a small wrapper around your own API calls to append normalised JSONL events into ~/.onwatch/api-integrations/, then open the API Integrations tab to monitor cumulative and recent usage. Full setup instructions live in docs/API_INTEGRATIONS_SETUP.md.

Menubar (macOS, Beta) -- The macOS build includes a menubar companion with two preset views:

  • Standard -- Provider cards with circular quota meters and reset metadata
  • Detailed -- Expanded provider cards with sparkline trends and full quota breakdowns

Configure it in Settings > Menubar. You can enable or disable the companion, pick the default view, change refresh and threshold settings, and drag providers into the order you want.

Menubar is currently in beta.

Email notifications (Beta) -- Configure SMTP to receive alerts when quotas cross warning or critical thresholds, or when quotas reset. Per-quota threshold overrides for fine-grained control. SMTP passwords are encrypted at rest with AES-GCM.

Push notifications (Beta) -- Receive browser push notifications when quotas cross thresholds. onWatch is a PWA (Progressive Web App) - install it from your browser for a native app experience. Uses Web Push protocol (VAPID) with zero external dependencies. Configure delivery channels (email, push, or both) per your preference.

Discord notifications -- Weekly pace alerts fire when a quota enters the red very-over-pace state and again after a configurable amount of additional usage, defaulting to 10 percentage points. Purple very-under-pace quotas are checked at configurable times, defaulting to 10:00 and 22:00. Both alert types can be enabled separately, and provider and account alerts are tracked independently.

Dark/Light mode -- Toggle via sun/moon icon in the header. Auto-detects system preference on first visit and persists your choice across sessions.

Password management -- Change your password from the dashboard. The hash is stored in SQLite and persists across restarts (takes precedence over .env). To force-reset, delete the row from the users table.

Single binary -- No runtime dependencies. All templates and static assets embedded via embed.FS. SQLite via pure Go driver (no CGO).


Who Is onWatch For?

Audience Pain Point How onWatch Helps
Solo developers & freelancers using Claude Code, Cline, Roo Code, or Kilo Code with Anthropic/Synthetic/Z.ai/Codex/Copilot/MiniMax/Gemini CLI/Antigravity Budget anxiety -- no visibility into quota burn rate, surprise throttling mid-task Real-time rate projections, historical trends, live countdowns so you never get throttled unexpectedly
Small dev teams (3-20 people) sharing API keys No shared visibility into who's consuming what, impossible to budget next month Shared dashboard with session tracking, cycle history for budget planning
DevOps & platform engineers Shadow AI usage with no FinOps for coding API subscriptions Lightweight sidecar (<50 MB), SQLite data source for Grafana, REST API for monitoring stack integration
Privacy-conscious developers in regulated industries Can't use SaaS analytics that phone home; need local, auditable monitoring Single binary, local SQLite, zero telemetry, GPL-3.0 source code, works air-gapped
Researchers & educators on grants Need per-session API cost attribution for grant reports and paper methodology Per-session usage tracking, historical export via SQLite
Budget-conscious API users paying $3-$60/month Every request matters; no way to know if plan is underutilized or budget is at risk Usage insights, plan capacity analysis, upgrade/downgrade recommendations via data

FAQ

How do I track my Synthetic API usage?

Install onWatch, set SYNTHETIC_API_KEY in your .env, and run ./onwatch. It polls the Synthetic /v2/quotas endpoint every 60 seconds, stores historical data in SQLite, and serves a dashboard at localhost:9211 showing subscription, search, and tool call quotas with live countdowns, rate projections, and reset cycle history.

How do I monitor Z.ai (GLM Coding Plan) API quota?

Set ZAI_API_KEY in your .env. onWatch polls the Z.ai /monitor/usage/quota/limit endpoint and tracks token limits, time limits, and tool call quotas. All providers can run simultaneously.

How do I track my Anthropic (Claude Code) usage?

onWatch auto-detects your Claude Code credentials from the system keychain (macOS) or credential file (Windows/Linux). Just install and run -- if Claude Code is installed, Anthropic tracking is offered automatically. You can also set ANTHROPIC_TOKEN manually in your .env. By default onWatch only re-reads credentials maintained by Claude Code and never rotates its OAuth refresh token. For independent refresh, authenticate a dedicated CLAUDE_CONFIG_DIR, set ANTHROPIC_TOKEN_ROTATION=on, and optionally set ANTHROPIC_CLAUDE_PATH. After that isolated token expires, onWatch runs one restricted Haiku probe through the official Claude executable, re-reads the rotated credentials, and retries polling. This probe consumes a small amount of Claude usage.

How do I track my Codex usage?

Set CODEX_TOKEN in your .env (recommended for Codex-only installs). You can retrieve it from ~/.codex/auth.json (tokens.access_token) or from $CODEX_HOME/auth.json if you use a custom Codex home. onWatch re-reads Codex credentials while running, so token rotation is picked up automatically. Full walkthrough: Codex Setup Guide.

How do I track my GitHub Copilot usage?

Set COPILOT_TOKEN in your .env with a GitHub Personal Access Token (classic) that has the copilot scope. Generate one at github.com/settings/tokens. onWatch polls the GitHub Copilot internal API to track Interactions, Chat, and Completions quotas with monthly reset cycle detection. Works with both free and paid plans. This feature is in beta and uses an undocumented API.

How do I track my MiniMax Coding Plan usage?

Set MINIMAX_API_KEY in your .env with your MiniMax Coding Plan API key. Get your key from the MiniMax Console. onWatch tracks the shared quota pool across all MiniMax models (M2, M2.1, M2.5) with 5-hour rolling window reset cycles. To track multiple MiniMax subscriptions, add additional accounts via the dashboard Settings > MiniMax > Add Account. Each account polls independently with its own API key and region. Full walkthrough: MiniMax Setup Guide.

How do I track my OpenRouter usage?

Set OPENROUTER_API_KEY in your .env with your OpenRouter API key. Get one from openrouter.ai/keys. onWatch polls the OpenRouter auth key endpoint to track total credits usage, daily/weekly/monthly spending, credit limits, and remaining balance. Works with both free and paid accounts.

Does onWatch work with Cline, Roo Code, Kilo Code, or Claude Code?

Yes. onWatch monitors the API provider (Synthetic, Z.ai, Anthropic, Codex, GitHub Copilot, MiniMax, OpenRouter, Gemini CLI, Cursor, or Antigravity), not the coding tool. Any tool that uses a Synthetic, Z.ai, Anthropic, Codex, Copilot, MiniMax, OpenRouter, Gemini CLI, Cursor, or Antigravity API key -- including Cline, Roo Code, Kilo Code, Claude Code, Codex CLI, Cursor, GitHub Copilot, MiniMax Coding Plan, OpenRouter, Gemini CLI, Antigravity, and others -- will have its usage tracked automatically.

Does onWatch send any data to external servers?

No. Zero telemetry. All data stays in a local SQLite file. The only outbound calls are to the Synthetic, Z.ai, Anthropic, Codex, GitHub Copilot, MiniMax, Gemini CLI, Cursor, and Antigravity quota APIs you configure (Antigravity connects to localhost only). Fully auditable on GitHub (GPL-3.0).

How much memory does onWatch use?

<50 MB under all conditions (typically ~34 MB idle, ~43 MB under heavy load). Measured with all nine agents (Synthetic, Z.ai, Anthropic, Codex, GitHub Copilot, MiniMax, Gemini CLI, Cursor, Antigravity) polling in parallel. Lighter than a single browser tab. See DEVELOPMENT.md for detailed benchmarks.


Architecture

                              ┌──────────────┐
                              │  Dashboard   │
                              │  :9211       │
                              └──────┬───────┘
                              ┌──────┴───────┐
                              │   SQLite     │
                              │   (WAL)      │
                              └──┬──┬──┬──┬──┬──┬──┬──┬─┘
       ┌───────────────────────┘  │  │  │  │  │  │  └───────────────────────┐
  ┌────┴─────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴─────┐
  │ Synthetic│ │  Z.ai   │ │Anthropic│ │  Codex  │ │ Copilot │ │ MiniMax │ │ Gemini  │ │Antigrav. │
  │  Agent   │ │  Agent  │ │  Agent  │ │  Agent  │ │  Agent  │ │  Agent  │ │  Agent  │ │  Agent   │
  └────┬─────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬─────┘
  ┌────┴─────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴─────┐
  │ Synthetic│ │  Z.ai   │ │Anthropic│ │chatgpt  │ │ GitHub  │ │ MiniMax │ │ Gemini  │ │ Local    │
  │  API     │ │  API    │ │OAuth API│ │OAuth API│ │Copilot  │ │  API    │ │CLI  API │ │ RPC      │
  └──────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └──────────┘

All agents run as parallel goroutines. Each polls its API at the configured interval and writes snapshots. The dashboard reads from the shared store.

Measured RAM (all eight agents running in parallel): ~34 MB idle, ~43 MB under heavy load. Single binary, all assets embedded via embed.FS.


CLI Reference

Portable data commands work the same on macOS, Linux, and Windows:

# Run this once on each source machine
onwatch data export --out mac-history.onwatch.zip
onwatch data export --out linux-history.onwatch.zip

# Copy both archives to the destination, then merge them
onwatch data import mac-history.onwatch.zip linux-history.onwatch.zip

Imports are additive and idempotent. Records from different installations are preserved even when their timestamps match, while reimporting the same originating record does not create a duplicate. Archives include history and an allowlist of non-secret settings only. See Portable Data Transfer for the merge and privacy rules.

Flag Env Var Default Description
--interval ONWATCH_POLL_INTERVAL 120 Poll interval in seconds (10--3600)
--port ONWATCH_PORT 9211 Dashboard HTTP port
--db ONWATCH_DB_PATH ~/.onwatch/data/onwatch.db SQLite database path
--debug -- false Foreground mode, log to stdout
--test -- false Isolated PID/log files for testing
--version -- -- Print version and exit

Additional environment variables:

Variable Description
ANTHROPIC_TOKEN Anthropic OAuth token (auto-detected from Claude Code)
ANTHROPIC_TOKEN_ROTATION OAuth refresh-token rotation (off by default; use on only with isolated credentials)
CLAUDE_CONFIG_DIR Optional isolated Claude profile directory (Windows/Linux)
ANTHROPIC_CLAUDE_PATH Optional path to the official Claude executable used by isolated refresh
CODEX_TOKEN Codex OAuth access token (recommended for Codex-only)
COPILOT_TOKEN GitHub Copilot PAT with copilot scope (Beta)
MINIMAX_API_KEY MiniMax Coding Plan API key
MINIMAX_REGION MiniMax region: global (default) or cn
GEMINI_ENABLED Enable Gemini CLI quota tracking (Beta, auto-detected)
GEMINI_REFRESH_TOKEN Gemini OAuth refresh token (for Docker/headless)
GEMINI_ACCESS_TOKEN Gemini OAuth access token (for Docker/headless)
GEMINI_CLIENT_ID Custom OAuth client ID (optional, has defaults)
GEMINI_CLIENT_SECRET Custom OAuth client secret (optional, has defaults)
CURSOR_TOKEN Cursor access token (auto-detected from Cursor Desktop)
ANTIGRAVITY_ENABLED Enable Antigravity provider (auto-detects local server)
ANTIGRAVITY_BASE_URL Antigravity base URL (for Docker/manual config)
ANTIGRAVITY_CSRF_TOKEN Antigravity CSRF token (for Docker/manual config)
SYNTHETIC_API_KEY Synthetic API key
ZAI_API_KEY Z.ai API key
ZAI_BASE_URL Z.ai base URL (default: https://api.z.ai/api)
ZAI_REGION Z.ai region: global (default) or cn
ONWATCH_ADMIN_USER Dashboard username (default: admin)
ONWATCH_ADMIN_PASS Initial dashboard password (default: devpass)
ONWATCH_LOG_LEVEL Log level: debug, info, warn, error
ONWATCH_HOST Bind address (default: 0.0.0.0)
ONWATCH_API_INTEGRATIONS_ENABLED Enable or disable API Integrations ingestion (default: true)
ONWATCH_API_INTEGRATIONS_DIR Directory onWatch tails for API Integrations JSONL events
ONWATCH_API_INTEGRATIONS_RETENTION How long detailed API Integration requests are kept before hourly compaction (default: 720h, or 30 days; 0 disables compaction)
ONWATCH_AGENT_USAGE_PRICING_JSON Optional LiteLLM-compatible pricing JSON for local agent token cost math
ONWATCH_CURSOR_USAGE_CSV Optional Cursor usage export CSV to ingest token usage into API Integrations

CLI flags override environment variables.


API Endpoints

All endpoints require authentication (session cookie or Basic Auth). Append ?provider=synthetic|zai|anthropic|codex|copilot|minimax|gemini|cursor|antigravity|both to select the provider.

Endpoint Method Description
/ GET Dashboard
/settings GET Settings page
/login GET/POST Login page
/logout GET Clear session
/api/current GET Latest snapshot with summaries
/api/history?range=6h GET Historical data for charts
/api/cycles?type=subscription GET Reset cycle history
/api/cycle-overview GET Cross-quota correlation at peak usage
/api/summary GET Usage summaries
/api/capabilities GET Build/runtime capabilities (platform, menubar)
/api/menubar/summary GET Normalized menubar snapshot payload
/api/menubar/test GET Browser-testable menubar page in test mode
/api/sessions GET Session history
/api/insights GET Usage insights
/api/providers GET Available providers
/api/settings GET/PUT User settings (notifications, SMTP, providers, menubar)
/api/data/export GET Download history and non-secret settings archive
/api/data/import POST Additively import one archive as multipart field file
/api/api-integrations/current GET Current aggregated usage by API integration
/api/api-integrations/history GET Chart-ready API integration history, ?range=
/api/api-integrations/health GET API integration ingest health and file state
/api/settings/smtp/test POST Send test email via configured SMTP
/api/password PUT Change password
/api/push/vapid GET Get VAPID public key for push subscription
/api/push/subscribe POST/DELETE Subscribe/unsubscribe push endpoint
/api/push/test POST Send test push notification
/api/update/check GET Check for new version
/api/update/apply POST Download and apply update

Self-Update

onWatch can update itself from the dashboard or CLI:

onwatch update    # Check for updates and self-update from CLI

Or click the update badge in the dashboard footer when a new version is available.

Under systemd, the update is fully automatic - no manual restart needed. onWatch detects its systemd service via /proc/self/cgroup, fixes the unit file if needed (Restart=always), runs systemctl daemon-reload, and triggers systemctl restart for a clean lifecycle-managed restart.

Standalone mode (macOS, or Linux without systemd) spawns the new binary, which takes over via PID file. If the spawn fails, onWatch automatically falls back to systemctl restart as a safety net.

The binary validates downloaded updates by checking executable magic bytes (ELF, Mach-O, PE) before replacing itself.

If a self-update fails to restart, the new binary is already on disk - just restart the service manually:

# systemd (Linux)
sudo systemctl restart onwatch

# Standalone (macOS / Linux without systemd)
onwatch stop && onwatch

Data Storage

~/.onwatch/
├── onwatch.pid          # PID file
└── data/
    ├── onwatch.db       # SQLite database (WAL mode)
    ├── .onwatch.log     # Main daemon log file (background mode)
    └── menubar.log      # Menubar companion log file (macOS menubar builds)

Log files are stored next to the database (default ~/.onwatch/data/). Each log rotates at 50 MB with 3 backups (.1, .2, .3) for both main and menubar logs.

On first run, if a database exists at ./onwatch.db, onWatch auto-migrates it to ~/.onwatch/data/.

Use Settings > Data to download a portable .onwatch.zip archive or import one or more archives. The same workflow is available from the terminal with onwatch data export and onwatch data import; it is safer than copying the live SQLite files. Provider credentials, login data, SMTP secrets, Discord webhooks, push keys, and OAuth tokens are excluded.


Docker Deployment

The container auto-detects the Docker environment and runs in foreground mode with stdout logging.

Note

onWatch provides Docker support with a distroless runtime image (~10-12 MB) and an Alpine variant (~15 MB) with shell access. You will almost certainly need to account for file permissions when using bind mounts for the SQLite database, as the container runs as non-root (UID 65532). See Storage below for details.

Quick Start

Using pre-built images from GitHub Container Registry:

Multi-arch images (linux/amd64, linux/arm64) are automatically built and published on each release:

# Pull and run the latest release (distroless - default)
docker run -d --name onwatch -p 9211:9211 \
  -v onwatch-data:/data \
  -e SYNTHETIC_API_KEY=your_key_here \
  ghcr.io/micsushi/onwatch:latest

An Alpine variant with shell access is also available for users who need docker exec (e.g. Codex multi-account setup):

# Alpine variant (includes /bin/sh)
docker run -d --name onwatch -p 9211:9211 \
  -v onwatch-data:/data \
  -e SYNTHETIC_API_KEY=your_key_here \
  ghcr.io/micsushi/onwatch:alpine
Tag Base Shell Size
latest, 2.x.y Distroless No ~10-12 MB
alpine, 2.x.y-alpine Alpine 3.21 Yes ~15 MB

Docker Compose (recommended):

git clone https://github.com/Micsushi/onWatch.git && cd onWatch
cp .env.docker.example .env
nano .env  # Add your API keys
docker-compose up -d
docker-compose logs -f

Using app.sh:

./app.sh --docker --build      # Build Docker image
./app.sh --docker --run        # Build + start container
./app.sh --docker --stop       # Stop container
./app.sh --docker --clean      # Remove container and image

Manual Docker run:

docker build -t onwatch:latest .
docker run -d --name onwatch -p 9211:9211 \
  -v ./onwatch-data:/data \
  --env-file .env \
  onwatch:latest

Configuration

Copy .env.docker.example to .env and set provider keys as needed. onWatch can start with no providers and you can enable them later from Settings. Key variables:

Variable Description Default
SYNTHETIC_API_KEY Synthetic API key --
ZAI_API_KEY Z.ai API key --
ZAI_REGION Z.ai region: global (default) or cn global
ANTHROPIC_TOKEN Anthropic token (auto-detected if not set) --
ANTHROPIC_TOKEN_ROTATION Let an isolated onWatch profile refresh through the official Claude executable off
CLAUDE_CONFIG_DIR Isolated Claude profile directory (Windows/Linux) --
ANTHROPIC_CLAUDE_PATH Optional official Claude executable path auto-detected
CODEX_TOKEN Codex OAuth access token (recommended; required for Codex-only) --
MINIMAX_API_KEY MiniMax Coding Plan API key --
MINIMAX_REGION MiniMax region: global (default) or cn global
GEMINI_REFRESH_TOKEN Gemini OAuth refresh token (Beta) --
ONWATCH_ADMIN_USER Dashboard username admin
ONWATCH_ADMIN_PASS Dashboard password devpass
ONWATCH_POLL_INTERVAL Polling interval (seconds) 120
ONWATCH_LOG_LEVEL Log level info

Storage

The container runs as non-root (UID 65532). The SQLite database is stored at /data/onwatch.db and must be persisted via a volume mount.

The docker-compose.yml uses a bind mount at ./onwatch-data/:

# Pre-create with correct ownership
mkdir -p ./onwatch-data && sudo chown -R 65532:65532 ./onwatch-data

Alternatively, use a named volume for simpler permissions handling:

docker run -d --name onwatch -p 9211:9211 \
  -v onwatch-data:/data \
  --env-file .env \
  onwatch:latest

Resource Limits

The docker-compose.yml includes memory limits (64M limit, 32M reservation), log rotation (10 MB, 3 files), and unless-stopped restart policy.

Troubleshooting

Database path is not writable: If startup shows database path is not writable, fix bind mount ownership recursively with sudo chown -R 65532:65532 ./onwatch-data or use named volumes. Container won't start: Check docker-compose logs -f; verify API keys in .env and port 9211 availability. Debugging: The default distroless image has no shell. Use the Alpine variant (ghcr.io/micsushi/onwatch:alpine) if you need docker exec access, or use a sidecar: docker run -it --rm --pid=container:onwatch --net=container:onwatch nicolaka/netshoot bash

Anthropic 429 rate limit errors: Anthropic's /api/oauth/usage endpoint has aggressive rate limits. onWatch waits for the next poll and does not rotate a valid token to bypass usage rate limits. OAuth refresh tokens are one-time use, so only one application may own each profile. See issue #16 and anthropics/claude-code#31021 for details.

Claude signs out in another app: Give onWatch, VS Code, and other applications different CLAUDE_CONFIG_DIR values and authenticate each directory separately. Never copy .credentials.json between profiles. Set ANTHROPIC_TOKEN_ROTATION=on only for a profile owned exclusively by onWatch. Keep it off when another process owns that profile.


Security

  • API keys loaded from .env, never committed, redacted in all log output
  • Session-based auth with cookie + Basic Auth fallback
  • Passwords stored as SHA-256 hashes with constant-time comparison
  • SMTP passwords encrypted at rest with AES-256-GCM (key derived from admin password)
  • VAPID keys auto-generated (ECDSA P-256) and stored in database
  • Web Push payloads encrypted per RFC 8291 (ECDH + HKDF + AES-128-GCM)
  • Parameterized SQL queries throughout

Development

See DEVELOPMENT.md for build instructions, cross-compilation, and testing.

./app.sh --build       # Production binary (macOS includes menubar) (or: make build)
./app.sh --test        # Tests with race detection (or: make test)
./app.sh --build --run # Build + run debug mode    (or: make run)
./app.sh --release     # Cross-compile all platforms (or: make release-local)

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Write tests first, then implement
  4. Run ./app.sh --test and commit with conventional format
  5. Open a Pull Request

License

GNU General Public License v3.0. See LICENSE.


Acknowledgments


Uninstall

Homebrew (macOS & Linux):

onwatch stop
brew uninstall onwatch
rm -rf ~/.onwatch

Manual install (macOS):

onwatch stop
rm -rf ~/.onwatch
sed -i '' '/# onWatch/d; /\.onwatch/d' ~/.zshrc ~/.bash_profile 2>/dev/null

Manual install (Linux):

onwatch stop
rm -rf ~/.onwatch
sed -i '/# onWatch/d; /\.onwatch/d' ~/.zshrc ~/.bashrc ~/.bash_profile 2>/dev/null
# If using systemd user service:
systemctl --user stop onwatch && systemctl --user disable onwatch && rm -f ~/.config/systemd/user/onwatch.service && systemctl --user daemon-reload
# If using systemd system service:
sudo systemctl stop onwatch && sudo systemctl disable onwatch && sudo rm -f /etc/systemd/system/onwatch.service && sudo systemctl daemon-reload

Windows (PowerShell):

.\onwatch.exe stop
$p = [Environment]::GetEnvironmentVariable("Path","User"); [Environment]::SetEnvironmentVariable("Path",($p -split ";" | Where-Object {$_ -notlike "*\.onwatch*"}) -join ";","User")
Remove-Item -Recurse -Force $env:USERPROFILE\.onwatch

About

Track AI API quotas across Synthetic, Z.ai, Anthropic (Claude Code), Codex, GitHub Copilot & Antigravity in real time. Lightweight background daemon (<50MB RAM), SQLite storage, Material Design 3 dashboard. Zero telemetry.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages