Skip to content

Latest commit

 

History

18 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UsageDock

AI usage limits + homelab node health in a floating side dock for macOS.

License: MIT macOS 12+ Swift 5.9+

UsageDock is a native, local-first macOS utility that hugs the left or right edge of your screen. A thin sliver sits on the edge; hover it and the dock slides out with circle meters for every API provider and health rings for your homelab nodes. Click a circle for that item's detail, or expand everything with the arrow. No Electron, no telemetry, no account.

Forked from NOTCHY by Ian Silva (MIT) — see License & attribution.

Features

  • Floating side dock — sliver → strip → expanded. Drag the strip to flip sides (left ↔ right, persisted). Click a circle for per-item detail; the arrow expands everything.
  • 10 providers, each showing what its dashboard shows: real % quota (Claude, Codex, OpenAI, Gemini, ElevenLabs, OpenCode Zen/Go) or credit balance (OpenRouter, DeepSeek, fal.ai, Perplexity).
  • CLI auto-detect — Claude, Codex and Gemini read the login your official CLI already stored. No key to paste.
  • Node diagnostics — CPU / RAM / disk / temperature / load / uptime / latency + top-5 processes for any machine running node_exporter.
  • In-app banners at 25 / 50 / 75 / 90 % usage. No system permission needed.
  • Refresh, settings and add-provider live in the dock strip toolbar (↻ refresh all · ⚙ settings · + add provider).
  • Keys live in the macOS Keychain, never in files or logs. Zero telemetry.

Requirements

Minimum
macOS 12.0 Monterey
Chip Apple Silicon (arm64)
Xcode CLT Any version with Swift 5.9+
Xcode (full) Optional — needed for the XcodeGen build path

Install

DMG release: grab UsageDock-Installer.dmg from the latest release, open it, and drag UsageDock into Applications. Builds are ad-hoc signed — on first launch: right-click the app → Open → Open, or xattr -dr com.apple.quarantine /Applications/UsageDock.app.

Or build from source (5 minutes):

xcode-select --install            # if you don't have CLT yet
brew install xcodegen             # project generation
git clone https://github.com/<you>/UsageDock.git
cd UsageDock/swift-project/UsageDock
xcodegen generate
xcodebuild -project UsageDock.xcodeproj -scheme UsageDock \
  -configuration Release -derivedDataPath build build
open build/Build/Products/Release/UsageDock.app

Use Release. Debug builds link a UsageDock.debug.dylib that crashes with a non-Apple signing identity. First Keychain read prompts once per provider — click Always Allow.

No-Xcode alternative: bash scripts/build.sh (swiftc + ad-hoc signed, local use only — never distribute that binary).

Connecting API providers

Open the dock → ⚙ → Providers (or + Add provider in the strip) and set up each one. Keys are stored in the Keychain and sent only to that provider.

Provider Shows How to connect
Claude 5 h session + weekly quota No key — auto-detects the Claude Code CLI login (Keychain Claude Code-credentials / ~/.claude/credentials.json). No CLI? Paste a claude.ai session cookie instead.
Codex ChatGPT-plan 5 h + weekly No key — npm i -g @openai/codex && codex login; reads ~/.codex/auth.json.
Gemini Code Assist per-model quota No key — sign in with the gemini CLI; reads ~/.gemini/oauth_creds.json. A plain API key gives "Connected" only.
OpenAI Monthly spend vs hard limit Paste an OpenAI API key (sk-…) from platform.openai.com.
OpenRouter Credits used vs purchased (%) Paste your OpenRouter API key; open the detail to see $used of $limit.
DeepSeek Remaining balance Paste your DeepSeek API key (platform.deepseek.com).
ElevenLabs Characters vs monthly limit Paste your ElevenLabs API key.
Perplexity Connected / desktop token Paste a Perplexity API key (or the desktop token).
fal.ai Credit balance Paste a fal key. Billing needs an admin-scoped key: a plain inference key is accepted by the API but returns 403 for GET /v1/account/billing — UsageDock then shows "Permission denied — this key cannot read account billing" instead of a misleading "expired" error. Rotate/scope the key at fal.ai/dashboard. Keys do not need a fal prefix.
OpenCode Zen/Go limits: rolling (5 h) + weekly + monthly Paste the auth cookie from opencode.ai (DevTools → Application → Cookies → opencode.ai). Fallback: if ~/.local/share/opencode/auth.json exists, OpenCode shows connected-only.

Claude / Codex / Gemini / OpenCode use undocumented internal endpoints that can change without notice. If one breaks, open an issue.

OpenCode Zen/Go — how it works

The provider reads the Go subscription limits from the opencode.ai dashboard's /_server RPC (Wasp "server-fn" wire format): a rolling ~5 h window plus weekly and monthly windows, each with a reset countdown — the same cards the dashboard shows. The RPC id/args are workspace-specific and deliberately not compiled in. Configure your own once:

defaults write com.usagedock.UsageDock opencodeZenGoURL \
  'https://opencode.ai/_server?id=…&args=…'

Capture it from your browser session: DevTools → Network → find the /_server call on the Go page → copy its full request URL. (The id query value is also the X-Server-Id header; the app handles that automatically.)

When the cookie expires the card shows "Sign in again" — paste a fresh cookie.

Monitoring nodes

UsageDock polls any machine that runs node_exporter (Prometheus text format, default port 9100) and shows a health card per node: CPU, RAM, disk, temperature, load, uptime, scrape latency, and top-5 processes.

1. Run node_exporter on the node

Docker quick start (adjust the bind address — prefer a Tailscale/WireGuard IP so the exporter is reachable only over your private network):

docker run -d --name node_exporter --restart unless-stopped \
  -p 100.64.0.1:9100:9100 \                       # <— your private IP, not 0.0.0.0
  -v /proc:/host/proc:ro -v /sys:/host/sys:ro -v /:/rootfs:ro \
  -v /home/<user>/node_exporter_textfile:/var/lib/node_exporter/textfile:ro \
  prom/node-exporter:v1.8.2 --path.rootfs=/host --path.procfs=/host/proc --path.sysfs=/host/sys \
  --collector.textfile.directory=/var/lib/node_exporter/textfile

--collector.textfile.directory is only needed for the top-5 process card (step 2); drop that mount if you don't want it.

2. Top-5 processes (optional)

A 30 s user systemd timer runs a collector that writes instantaneous CPU/MEM deltas (from /proc, not ps — ps %CPU is a lifetime average) into a .prom file the exporter exposes:

# on the node, as the user (no root needed):
mkdir -p ~/node_exporter_textfile ~/.local/bin ~/.config/systemd/user
install -m 755 docs/node-top5.py ~/.local/bin/node-top5.py
sed 's|^OUT = .*|OUT = "'"$HOME"'/node_exporter_textfile/processes.prom"|' \
  docs/node-top5.py > ~/.local/bin/node-top5.py
# fix the textfile mount above to match this path
cp docs/node-top5.{service,timer} ~/.config/systemd/user/
XDG_RUNTIME_DIR=/run/user/$(id -u) systemctl --user enable --now node-top5.timer

The .prom file must be world-readable (chmod 644) because the exporter container runs as nobody.

3. Add the node in UsageDock

⚙ → Nodes → Add node — name, host (IP or hostname), port. Poll interval: 10 s / 30 s / 1 min. Host may hold several addresses — tailnet IP, LAN IP, mDNS name — comma-separated; UsageDock tries them in order and uses the first that answers, so a node survives a logged-out VPN or a new DHCP lease. The card shows the address that actually answered. Config persists in ~/Library/Application Support/UsageDock/nodes.json (local-only — node addresses never leave your machine or get committed).

macOS 15+ gates LAN traffic behind Local Network privacy: without the grant macOS fails the connection with Local network prohibited (visible via log show --predicate 'process == "UsageDock"') while a tailnet address keeps working. Allow UsageDock under System Settings → Privacy & Security → Local Network, and keep NSLocalNetworkUsageDescription in the Info.plist.

CPU% is computed from counter deltas between polls (first scrape shows —).

Using the dock

Action Result
Hover the edge sliver Strip slides out with circle meters
Click a circle Detail for that provider/node only
Click the top/bottom arrow Expand everything (all providers + node cards)
Drag the strip across the edge Flip dock side (persisted)
↻ / ⚙ / + in the strip Refresh all / settings / add provider
Click outside Collapse

Circle meters: real % arc for quota providers, full ring + balance amount for balance providers, checkmark for connected-only. Nodes show a health ring (colour-coded by the worst metric).

Building from source

cd swift-project/UsageDock
brew install xcodegen
xcodegen generate
xcodebuild -project UsageDock.xcodeproj -scheme UsageDock \
  -configuration Release -derivedDataPath build build \
  CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY="UsageDock Dev Signer"
open build/Build/Products/Release/UsageDock.app
  • Stable signing identity (one-time): a self-signed cert makes the Keychain "Always Allow" stick across rebuilds. Without it, ad-hoc signing changes the identity every build and macOS re-prompts per credential.
  • ATS: the only plain-HTTP traffic is your own node_exporter instances (raw IPs), which ATS has no exception mechanism for — so NSAllowsArbitraryLoads is enabled. Node metrics stay on your network.
  • Tests: scripts/test.sh — a swiftc harness compiling the real sources (hosted XCTest crashes on LSUIElement apps, so tests run headless via the harness pattern). Run it after any provider/node change.

Privacy & security

  • Provider credentials live in the macOS Keychain (com.usagedock.UsageDock service — migrated from the pre-rename com.notchylimit.NotchyLimit service). Never logged, never in the repo.
  • Zero telemetry: the app talks only to the providers you configure and the nodes you add.
  • Node metrics are plain-HTTP by design — bind node_exporter to a private (Tailscale) address, never 0.0.0.0.

License & attribution

MIT. Fork of NOTCHY by Ian Silva; upstream code keeps its MIT copyright, fork additions are MIT. See LICENSE / swift-project/UsageDock/LICENSE.

About

UsageDock is a native, local-first macOS utility that hugs the left or right edge of your screen. A thin sliver sits on the edge; hover it and the dock slides out with circle meters for every API provider and health rings for your homelab nodes.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages