Skip to content

EdmundLimBoEn/UsageWidget

Repository files navigation

UsageWidget

UsageWidget is a self-hosted iOS 26+ app and large Home Screen widget for monitoring CodexBar usage. A small Go service runs on Linux, macOS, or Windows, normalizes every enabled provider, stores history in SQLite, and sends APNs alerts and WidgetKit refreshes to the phone.

┌──────────────┐   Tailscale HTTPS    ┌──────────────────┐   Unix socket   ┌─────────────────┐
│ iPhone app   │ ───────────────────► │ usagewidgetd     │ ──────────────► │ CLI collector   │
│ + widget     │ ◄── APNs/WidgetKit ─ │ Go API + SQLite  │                 │ CodexBar session│
└──────────────┘                      └──────────────────┘                 └─────────────────┘

Linux uses the isolated Unix-socket collector shown above. Native macOS and Windows runs use an exact CodexBar CLI path or a configured CodexBar HTTP URL.

What it does

  • Discovers Codex, Claude, Grok, and other providers from CodexBar instead of hard-coding a provider list.
  • Shows every rate window, reset time, remaining capacity, freshness state, and a burn-rate forecast after enough history has accumulated.
  • Supports global, per-provider, and per-window alert rules, quiet hours, and optional danger reminders.
  • Detects threshold crossings, scheduled resets, early “Tibo” resets, and reset-credit increases without alerting on a first-seen baseline.
  • Keeps provider visibility and ordering on the server, so hidden providers are omitted from phone-facing snapshots and alerts.
  • Onboards the iPhone by manual entry or a private setup QR generated by the installer.
  • Runs from Linux, macOS, or Windows and installs the native server on a Linux, macOS, or Windows SSH target—no repository clone or local build required.
  • Detects amd64/arm64 automatically and preserves configuration and SQLite data when the installer is rerun for an update.
  • Includes redacted health and device-readiness checks plus a targeted APNs and WidgetKit delivery test.

Provider credentials never leave the machine running CodexBar. On Linux they remain isolated in the collector account; desktop mode runs as the signed-in user. The phone API does not return raw CodexBar payloads. The app and widget share the bearer token through a Keychain access group; App Group defaults contain only cached display data and preferences.

Built with Codex and GPT-5.6

UsageWidget began with a personal frustration. I first used Codex models through Claude Code, where I could not reliably see my usage, and I had to keep checking X to learn when limits might reset. CodexBar made the data available on my Mac, but it did not provide the mobile experience I wanted: a quick look at my phone while away from my desk, without opening my Mac and navigating through several menus. That led to the iOS app, Home Screen widget, reset timings, forecasts, and alerts in this repository.

Codex was part of the project from its beginning. I used it to plan and implement work across the SwiftUI and WidgetKit frontend, Go server, Linux deployment, Cloudflare-hosted installer endpoint, landing page, tests, and TestFlight release preparation. It was particularly valuable near release, when it could inspect the repository as a whole for integration, security, and packaging problems that were difficult to spot one file at a time.

The clearest example of work completed with GPT-5.6 is the installation and onboarding flow. An initially manual sequence of server commands became a single curl-based installation path followed by QR-code setup in the iOS app. This required coordinated changes to the installer, server configuration, Cloudflare Worker routing on the project domain, QR payload, and iOS registration flow. GPT-5.6's ability to plan and carry a long task across those boundaries made that integration practical.

I remained responsible for the product direction and final decisions. I chose the problem, the self-hosted Linux architecture, the privacy boundary, the UI and visual preferences, and the behavior I wanted from resets and alerts. My workflow was iterative: use each working version, identify what felt wrong, then direct Codex to revise it. I accepted, changed, or rejected its suggestions based on how the complete system behaved rather than treating generated code as finished work.

Verification combined automated Go, installer, CLI, and iOS build checks with hands-on testing on physical iPhones running iOS 26 and iOS 27. That process also uncovered a server data-fetching approach from an earlier implementation that interacted poorly with rate limits; Codex helped trace it across the collection path and replace it with the current design.

Codex and GPT-5.6 were development tools, not runtime dependencies. UsageWidget does not send requests to GPT-5.6: it reads usage data from CodexBar and serves that data through the self-hosted system described below. Without Codex and GPT-5.6, UsageWidget would not have existed.

For Build Week verification, the primary Codex /feedback session is 019f74b0-fe97-7dc3-bb40-0e7b5c0274a7. The public source repository is github.com/EdmundLimBoEn/UsageWidget.

Repository map

Path Purpose
server/ Go service, collector, SQLite store, event engine, APNs, and HTTP APIs
ios/ SwiftUI app, WidgetKit extension, shared models, and XcodeGen project
cli/usagewidget Local/server operations CLI
server-install.sh Linux release installer and usagewidget-admin lifecycle commands
server-setup.sh Interactive Mac-to-Linux source installation
server/deploy/start-* Native macOS and Windows foreground launchers
docs/ Historical implementation plans and design records

Install a server

Host Support Upstream source
Ubuntu 22.04/24.04, Debian 12 Production service install (amd64/arm64) Isolated CodexBar CLI collector
macOS Native foreground server (Intel/Apple silicon) Local CodexBar CLI or CODEXBAR_URL
Windows 10/11 Native foreground server (amd64/arm64) CODEXBAR_URL or a compatible CLI build

The managed Linux service remains the recommended always-on deployment because it provides service management and separates the server from provider credentials. Desktop packages are useful for personal machines, testing, and hosts that are already kept signed in.

The hosted installer supports the full controller-to-target matrix: run it from Linux, macOS, or Windows and point it over SSH at Linux, macOS, or Windows. Linux and macOS controllers share the Unix command; Windows uses PowerShell:

# Linux or macOS
curl -fsSL https://usagewidget.edmundlim.systems/install.sh | bash
# Windows PowerShell
irm https://usagewidget.edmundlim.systems/install.ps1 | iex

Both commands ask for the SSH user and target, detect the target OS and amd64/arm64 architecture, verify the matching GitHub release on that target, configure its private Tailscale route, and print the iPhone setup QR back in the controller terminal. Existing configuration and databases are retained.

Linux

The supported release hosts are Ubuntu 22.04, Ubuntu 24.04, and Debian 12 on amd64 or arm64. The host needs systemd, Tailscale, an unprivileged account with a working CodexBar session, and root or sudo access for installation.

The fastest setup downloads the latest release for the host architecture, verifies its checksum, installs both services, and prints the private iPhone setup QR:

curl -fsSL https://usagewidget.edmundlim.systems/install.sh | bash

The installer first asks for the SSH destination, then which unprivileged Linux account owns the working CodexBar session. It invokes sudo only for remote system installation steps. You do not need to clone this repository or add command-line flags. When installation finishes, scan the QR in UsageWidget; the server URL and generated bearer token are encoded for you. The installer can be rerun safely for updates while preserving configuration and SQLite data. This command is for the supported Ubuntu and Debian server hosts.

If you already downloaded and extracted a release bundle, run the packaged installer directly instead:

sudo ./server-install.sh install --collector-user YOUR_LOGIN
sudo usagewidget-admin doctor
sudo usagewidget-admin qr

The installer verifies the release contents, installs CodexBar when needed, preserves configuration and SQLite data on reruns, binds the API to 127.0.0.1:8377, configures the Tailscale Serve /usagewidget route, and prints a setup QR when qrencode is available.

From a development Mac or Linux machine, the interactive source-install path builds the correct server architecture and runs the same installer remotely:

./server-setup.sh

See the deployment guide for prerequisites, APNs configuration, backups, updates, recovery, and manual installation. See the redeploy runbook for routine source deployments.

macOS

From a Linux or macOS controller, run the no-flag Unix installer and select the target Mac over SSH (Windows controllers use the PowerShell command above):

curl -fsSL https://usagewidget.edmundlim.systems/install.sh | bash

It detects Intel or Apple silicon remotely, verifies and installs the matching release under ~/Library/Application Support/UsageWidget/App, registers a LaunchAgent, configures Tailscale Serve, and prints the QR locally. It uses a working target-side codexbar/CodexBarCLI or a private CodexBar usage URL. Saved configuration and SQLite data remain in the parent UsageWidget directory and survive application updates. Keep the terminal open. For a source checkout, build first with (cd server && go build -o ../bin/usagewidgetd ./cmd/usagewidgetd), then run server/deploy/start-macos.sh with USAGEWIDGET_DAEMON pointing to that binary.

Windows

From a Windows controller, run the native no-flag installer and select the target Windows machine over SSH (Linux/macOS controllers use the Unix command):

irm https://usagewidget.edmundlim.systems/install.ps1 | iex

It detects x64 or ARM64 remotely, verifies and installs the native bundle under %LOCALAPPDATA%\UsageWidget\App, registers a persistent scheduled task, configures Tailscale Serve, and prints the QR locally. It asks for a private CodexBar usage URL when no compatible target-side CLI exists. The SQLite database and configuration stay in %LOCALAPPDATA%\UsageWidget, outside the replaceable application directory. CodexBar currently publishes standalone CLI archives for macOS and Linux, not Windows, so Windows normally uses a private CodexBar HTTP endpoint on another machine. A compatible Windows CLI can instead be selected with -CodexBarBin C:\path\to\codexbar.exe. Do not expose that upstream endpoint or UsageWidget port 8377 publicly.

Local server development

Go 1.26.5 or newer is required by server/go.mod.

cd server
export USAGEWIDGET_TOKEN="$(openssl rand -hex 32)"
export CODEXBAR_URL=http://127.0.0.1:8765/usage
go run ./cmd/usagewidgetd

The data source is selected in this order:

  1. CODEXBAR_CMD, a legacy command override for an account that owns the provider sessions.
  2. CODEXBAR_URL, an HTTP development override.
  3. CODEXBAR_BIN, an exact CLI path used by native desktop installs.
  4. COLLECTOR_SOCKET, the Linux production default (/run/usagewidget/codexbar.sock) served by the isolated collector.

Disabled CodexBar providers remain absent instead of becoming permanent error rows.

Connect the iPhone

cd ios
xcodegen generate
open UsageWidget.xcodeproj

Select your Apple Development team and build to an iOS 26+ device. In the app, scan the installer QR or enter the Tailscale HTTPS base URL and bearer token, then test the connection. Grant notification permission and add the Usage large widget from the Home Screen gallery.

Unsigned CI build:

xcodebuild -scheme UsageWidget -destination 'generic/platform=iOS' \
  CODE_SIGNING_ALLOWED=NO build

The default identifiers are:

  • App Group: group.systems.edmundlim.usagewidget
  • App: systems.edmundlim.UsageWidget
  • Widget: systems.edmundlim.UsageWidget.widget

Replace them before distributing your own build. WidgetKit timelines and push refreshes are system-budgeted; a one-minute server poll does not guarantee a one-minute Home Screen redraw.

Operations CLI

Install or link cli/usagewidget into your PATH. Local configuration is read from ~/.config/usagewidget/env with mode 600; the installed server CLI automatically uses /etc/usagewidget/env and the loopback API.

usagewidget env sync          # copy the server token into local config
usagewidget health
usagewidget snapshot
usagewidget settings
usagewidget qr                # show the private iPhone setup QR again
usagewidget poll              # force a real collection cycle
usagewidget deploy
usagewidget logs -f
usagewidget status

Run usagewidget help for the complete command list and environment variables.

HTTP API

Every main API route requires Authorization: Bearer <USAGEWIDGET_TOKEN>.

Method Path Purpose
GET /v1/health Redacted service, collector, database, polling, APNs, and delivery health
GET /v1/snapshot Visible normalized providers, windows, forecasts, and freshness
GET / PUT /v1/settings Polling, provider display, and alert-rule settings
POST /v1/devices Register or rotate APNs and WidgetKit tokens
DELETE /v1/devices/{deviceID} Remove a registered device
POST /v1/poll Force one collection cycle
GET /v1/readiness/{deviceID} Get redacted server and device readiness checks
POST /v1/readiness/{deviceID}/test Send a targeted audible alert and widget delivery test

Alerts and forecasts

Defaults are a five-minute poll, an early alert at 10% used, a danger alert at 10% remaining, no repeated danger reminders, and quiet hours disabled. Allowed poll intervals are 1, 5, 15, 30, and 60 minutes; repeat intervals are never, hourly, every three hours, or every six hours.

Rules inherit from global → provider → window. Hiding a provider removes it from the widget and disables its alerts. During quiet hours, automatic alerts are delivered passively; outside quiet hours they remain audible. The explicit readiness test is always audible.

Forecasts use up to 24 hours of samples from the current reset cycle and appear only after at least three increasing samples spanning 30 minutes and one percentage point. Forecasts are omitted from stale snapshots.

Release verification

Use Release readiness in the iOS app to inspect server, APNs, and device registration state and to send a targeted audible alert and widget refresh. Run go test ./... in server/, bash tests/installer_test.sh, and the unsigned Xcode build above before producing a distribution archive.

TestFlight archive and export:

cd ios
xcodebuild -project UsageWidget.xcodeproj -scheme UsageWidget \
  -configuration Release -destination 'generic/platform=iOS' \
  -archivePath build/UsageWidget.xcarchive \
  -allowProvisioningUpdates archive
xcodebuild -exportArchive \
  -archivePath build/UsageWidget.xcarchive \
  -exportPath build/TestFlight \
  -exportOptionsPlist ExportOptions.plist \
  -allowProvisioningUpdates

Release tags (v*) run Go tests, shell syntax checks, installer tests, and build Linux, macOS, and Windows amd64/arm64 bundles through GitHub Actions.

Security and human steps

Read SECURITY.md before exposing a service or publishing the repository. Apple signing, APNs, private networking, release publication, and device verification steps are tracked in HUMANS.md.

About

No description, website, or topics provided.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors