Skip to content
On this page

Token Horizon documentation

Token Horizon tracks AI token usage, cost, plan limits and local model performance from provider-reported data. Use the native macOS app or the desktop dashboard for Windows and Linux. Local tracking works without a Token Horizon account; leaderboard and team sharing are optional.

Overview

On macOS, the app runs as a notch panel or menu-bar item. On Windows and Linux, it opens a desktop dashboard with its portable core included. Both expose the local HTTP API at 127.0.0.1:8765; GET /health identifies the running version and build. The notch, WidgetKit extension, and native Mac integrations described later on this page belong to the macOS app.

Install

Choose a macOS download, Windows download, or Linux download. The download selector shows packages available for each platform. Windows and Linux installers include the core and runtime, so destination machines do not need Go, Node.js, or a separate daemon installation.

If the selector reports no published Windows or Linux package, check the Desktop Linux and Windows workflow for a successful review build. When a completed run has downloadable artifacts, download the one matching your machine and extract its ZIP. GitHub requires sign-in to download workflow artifacts; this is separate from using Token Horizon locally. Review builds do not install automatic updates, and Windows review installers are unsigned.

macOS · Apple silicon

One line, no sudo:

curl -fsSL https://raw.githubusercontent.com/castlemilk/token-horizon/main/install.sh | bash

Homebrew:

brew tap castlemilk/tap
brew install --cask token-horizon

Releases are Developer ID signed and Apple-notarized (Gatekeeper reports source=Notarized Developer ID). Local builds via ./scripts/make-app.sh are ad-hoc signed.

Windows 10 or 11 · x64

  1. Choose Windows x64 in the download selector and download the TokenHorizon-…-windows-x64-setup.exe installer.
  2. Run the installer, choose your installation folder, and complete the setup wizard. Installation is for your Windows user and does not require administrator access.
  3. Open Token Horizon from the Start menu or desktop shortcut. Follow capture setup to select which local usage source to count.

The Windows installer supports x64 machines. Keep provider clients and their histories in the same Windows user profile used to run Token Horizon.

Linux · x64 or ARM64

Use a graphical Linux desktop session and choose the package matching your processor: x64 / amd64 for Intel or AMD, or ARM64 / aarch64 for ARM. Run uname -m if unsure. Download a Debian .deb package or portable .AppImage from the Linux selector.

Debian or Ubuntu: install the downloaded package, then open Token Horizon from your application launcher or run token-horizon. Replace VERSION with the version in the downloaded filename; use arm64 in place of amd64 for ARM:

sudo apt install ./TokenHorizon-VERSION-linux-amd64.deb
token-horizon

AppImage: make the downloaded file executable, then launch it. Replace VERSION with your downloaded version and x86_64 with arm64 when appropriate:

chmod +x TokenHorizon-VERSION-linux-x86_64.AppImage
./TokenHorizon-VERSION-linux-x86_64.AppImage

AppImage needs FUSE 2 support and Chromium user-namespace sandbox support. If FUSE 2 is missing on Ubuntu 24.04, install libfuse2t64; Ubuntu 22.04 uses libfuse2. See the AppImage FUSE guide for other distributions.

# Ubuntu 24.04, if FUSE 2 is missing
sudo apt install libfuse2t64

If your Debian or Ubuntu desktop restricts user namespaces and AppImage reports a sandbox error, install the .deb package. Its installer configures the bundled AppArmor profile and sandbox helper. Keep the app sandbox enabled.

First-run capture setup · Windows and Linux

A fresh desktop installation starts in point mode and counts requests routed through consented local meters. An empty activity view is expected until a meter is configured or you opt in to provider-file capture. The current desktop UI links to setup instructions; configure capture in the local files below.

Quit Token Horizon, open its configuration folder, and create or edit settings.json and consents.json. Preserve existing keys and restart the app after editing. Default folders:

TH_CONFIG_DIR overrides these locations if you configured it yourself.

Read existing provider history: in settings.json, select files mode:

{
  "captureMethodology": "files"
}

In consents.json, grant only the file-reading scope:

{
  "fileReading": { "granted": true, "version": 1 }
}

Restart Token Horizon and allow about 30 seconds for the first history import. Files mode counts provider-reported usage from supported CLI histories on this machine, then imports updates every 15 minutes while the core runs. It does not start request meters. The native macOS app discovers its supported histories directly and uses its own setup flow.

Meter live OpenAI requests: instead, select point mode and enable the OpenAI meter in settings.json:

{
  "captureMethodology": "point",
  "meterToggles": { "openai": true }
}

Grant the metering scope in consents.json:

{
  "metering": { "granted": true, "version": 1 }
}

Restart the app and set your OpenAI client or SDK's base URL to http://127.0.0.1:9242/v1. Keep its normal provider credentials in that client. Only requests sent through the local meter are counted; other providers have separate meter ports listed at GET /meters. Meter traces are stored locally and can include request and response bodies; set "traceCapture": false in settings.json to disable body capture while retaining usage counts.

Opening the desktop app starts its core on 127.0.0.1:8765, or attaches to a compatible core already there. Closing the app stops the core it started and preserves your saved history. An independently running service remains available. Local tracking needs no account; enable cloud sharing separately if you want to publish usage.

The macOS panel

Hover the notch (or click the menu-bar rings) to open the panel. Tabs:

TabShows
TOKENSToday's tokens/cost, per-model and per-tool breakdown, cache rates, plan-limit bars with reset countdowns, streaks and league standing.
ACTIVITYCPU, memory, disk and network history, top processes by CPU/memory/disk/network, Docker containers, and MLX runner telemetry (measured tok/s).
MLXLocal model runners (MLX/Ollama) with per-process telemetry, bounded history, and measured throughput.
ENGINELocal inference backends (Splash and TH Engine) — hardware fit, model catalog, serve/stop, live throughput, benchmark results.
LeaderboardYour published standing, leagues/MMR, sync and share controls.
ShellsRecent shell execution events captured by the zsh hooks.
SettingsSurface mode (auto/notch/tray), sharing rules, history persistence, start-at-login, and the serving build stamp.

macOS desktop widget

The app ships a native WidgetKit extension (macOS 14+), embedded at build time by scripts/make-widget.sh. Add it from the desktop's Edit Widgets gallery in small, medium or large. It has a 3-page carousel — usage, plan limits, plans & resets — and a segmented time-window picker h · d · w · m · y (24 hourly bars, 7 daily, 17 weekly, 30 daily, 12 monthly). The GitHub-style activity heatmap always matches the selected window, and the plans page spotlights what expires soonest with urgency colors (<24h red, <48h orange). Configure it in Settings → Desktop widget (enable, period, accent, show cost/limits/chart) with a live preview; every control writes the same app-owned preference the widget reads, so preview, widget and app never disagree. The extension consumes the versioned snapshot at GET /widget and falls back to its last cached copy when the app is down.

Usage tracking

Parsers read the tools' own session data on your machine:

Accuracy rule: token counts are provider-reported or absent. Costs come from the catalog's published rates — never estimated from resource usage, never extrapolated. When a number isn't published, the UI shows —.

Plan limits

Limit fetchers query each provider's own plan APIs (or parse their local credentials) for 5-hour, weekly and monthly windows. Auth sources, checked in order:

ProviderSource
AlibabaALIBABA_TOKEN_PLAN_COOKIE / Settings cookie
KimiKIMI_CODE_HOME/KIMI_HOME, ~/.kimi*/credentials/kimi-code.json (OAuth refresh)
GLM / MiniMax / OpenCode Goopencode auth.json keys
OpenAI / Codexopencode auth.json OAuth + live usage API, JSONL fallback
Claude~/.claude* profiles → credentials file → Keychain
Gemini / AGY~/.gemini*/oauth_creds.json + Antigravity language-server telemetry
DeepSeekDEEPSEEK_API_KEY or opencode auth

Model catalog & plans

The catalog merges models.dev, OpenRouter, live provider APIs and curated benchmarks into one family per model. The web list at /models is a static export of the app's own merge pipeline — regenerated on a schedule so app and web can't drift.

Pricing evidence is explicit, in three states:

Subscription plans live in a curated artifact with verified usage tiers (Copilot, Kimi Code, MiniMax Token Plan, GLM Coding Plan, OpenCode Zen, and more). See the Plans view and the blog post Pricing evidence.

The Cheapest Inference view ranks model families by the best price across every provider listing we track (OpenRouter, models.dev gateways and resellers), with the discount against the direct rate and a price-leaders board — so "cheap inference" is answerable from the same catalog instead of a separate scrape.

Leaderboard & privacy

Publishing is opt-in. The leaderboard ranks token volume across periods, with leagues/MMR, streaks, efficiency, per-model breakdowns and share cards. What's published: totals, model/tool breakdowns, projects, hardware, sessions with titles only if you enable leaderboardSharePrompts (default off — rows publish with empty titles so activity timelines still work). Prompt history itself never leaves the individual profile. Profile links (/u/<handle>) show a dynamic preview with token totals, a 17-week heatmap, streak and provider mix. Shared reports (/s/<id>) respect their audience, privacy options and expiry. Restricted links require sign-in and show a generic preview; enable the public-link option to let anyone with the link view the report.

Open your usage on the web: in the desktop app, set your handle under Settings → Leaderboard (or Sharing & teams in newer builds), save it, and use Publish or Sync now. Open that handle’s public profile on the web and use Claim to link it to your signed-in Google or GitHub account. Keep the original claim token if one was issued. Workspace discovers profiles linked to that exact account; signing in alone does not upload activity. A claimed profile’s later uploads require an authorized app write token.

Workspace analyzes one profile’s published token totals, model cost estimates and shared activity. Cost estimates are not provider invoices. Public previews expose only published fields; live traces, device telemetry and plan limits remain in the desktop app.

HTTP API

Local daemon on 127.0.0.1:8765 (CORS enabled for the dashboard):

EndpointReturns
GET /healthServing build stamp, gateway/proxy ports.
GET /statsUsage snapshot, models, tools, projects, system stats.
GET /trends?window=1D|1W|1M|3M|1YRolling token history with tool breakdown.
GET /limitsProvider plan limits with reset times.
GET /claude/accountsClaude profiles, orgs, live quota.
GET /models · /top-picksFiltered catalog rows · ranked value picks.
GET /models/catalogFull web catalog export (models, plans, top picks).
GET /history?days=Daily history with per-provider breakdown.
GET /activity/heatmap?days= · /projects · /achievementsHeatmap grid · project rollups · achievements + season.
GET /processes · /process?pid= · /dockerTop processes, deep PID inspection, containers.
GET /events?limit= · POST /eventShell events (zsh hooks).
GET /metricsPrometheus text (OTel meter).
GET /widget · POST /widget/window?value=Desktop-widget snapshot (v5 stacks + heatmap + limits) · set the chart window.
GET /proxy/config · GET /tracesGateway sidecar config · captured traces.
GET /engine · POST /engine/serve · POST /engine/stopLocal-engine state · serve a model {backend?, model, tokenizer?, max_memory_gb?, max_context_k?} · stop a backend.
GET /engine/benchLatest side-by-side engine benchmark (scripts/bench-engines.sh).
POST /cache/reset · GET /cacheReset / inspect the durable cache.

Hosted edge API on https://token-horizon.dev: /api/leaderboard, /api/user/:handle, /api/providers, /api/teams, /api/season, /api/models/catalog, /api/models/usage, /api/config, /api/health, /api/share (SVG badges).

MCP server

Two MCP servers ship in mcp/:

Catalog tools (both servers): search_models (token_horizon_catalog in the standalone server) and get_plans (token_horizon_plans). They search the unified catalog with pricing evidence, benchmark scores, context windows and plan linkage, and return plan tiers with included models. Local daemon first, hosted catalog fallback; set TH_CATALOG_URL to pin a source.

{
  "mcpServers": {
    "token-horizon": {
      "command": "node",
      "args": ["~/.config/token-horizon/mcp/token-horizon-mcp.mjs"]
    }
  }
}

LLM gateway

The app ships with a Go sidecar (token-horizon-gateway) that speaks OpenAI and Anthropic protocols on one local port and captures full conversation traces. Point a client at the gateway by swapping its base URL:

Traces are stored locally (bounded: 256KB per side, 30 day-files, 256MB) and served at GET /traces. Gateway traffic never feeds usage totals — the file parsers already count it.

Local engines

Two supervised inference backends run as sidecars — attach-or-spawn, never in-process. A server you started yourself is adopted read-only and never killed; the app only stops processes it spawned.

TH Engine extras for the qwen3_5 backend:

Route clients through the gateway prefixes /th-splash/ and /th-engine/ so local inference is traced like any other provider. Compare the two with scripts/bench-engines.sh — results land at GET /engine/bench and in the ENGINE tab.

Shell helper

The zsh integration installs a th command and preexec/precmd hooks:

th status          # daemon + gateway status
th limits          # provider plan windows
th leaderboard     # standing and sync controls
th share           # generate a share card
th reset-cache     # clear and rebuild the durable cache

Troubleshooting

SymptomFix
Model list looks stale / commit mismatchcurl -s localhost:8765/health and compare build.commit with git rev-parse --short HEAD. Rebuild with ./scripts/make-app.sh.
API missing on :8765Check the LaunchAgent: /Applications/TokenHorizon.app/Contents/MacOS/TokenHorizon --agent-status. The app never runs API-less — a bind failure exits loudly.
llm_gateway_port nullRebuild via make-app.sh; the gateway sidecar is bundled and health-gated at launch.
Provider limits silently missingCredentials expired — re-auth with the provider (opencode keys rotate, Claude reads stored tokens, Alibaba needs a fresh cookie).
Catalog/plans stale on the webThe static export is refreshed on a schedule; run task models-refresh locally to regenerate docs/data/models.json.