Skip to content

Add opt-in local server for a companion mobile app#246

Open
mahdi-salmanzade wants to merge 1 commit into
hamed-elfayome:mainfrom
mahdi-salmanzade:feat/local-companion-server
Open

Add opt-in local server for a companion mobile app#246
mahdi-salmanzade wants to merge 1 commit into
hamed-elfayome:mainfrom
mahdi-salmanzade:feat/local-companion-server

Conversation

@mahdi-salmanzade

Copy link
Copy Markdown

What

Adds an opt-in, read-only local HTTP server so a companion mobile app can display your current usage over the local network. This implements the "Mobile App" settings tab, replacing the previous coming-soon placeholder with a real pairing screen.

A matching open-source Expo app lives here: https://github.com/mahdi-salmanzade/claude-usage-mobile

How it works

Claude API ──sessionKey──▶ this app (LocalServerService)
                                │  GET /v1/usage  (Bearer token, read-only)
                                ▼
                          phone (companion app) over LAN / Tailscale

The server simply serves the already-computed DataStore.shared.loadUsage() snapshot as JSON. The phone never authenticates to Claude — the session key never leaves the Mac.

Security model

  • Disabled by default — the user must explicitly enable it in Settings → Mobile App.
  • Token-gated — every request requires Authorization: Bearer <token>; the token is generated locally and only shared via the pairing QR code. Regenerating it un-pairs existing devices.
  • Read-only — single data endpoint (GET /v1/usage) plus GET /v1/ping; unknown paths → 404, bad/missing token → 401.

Changes

  • LocalServerService.swift (new) — NWListener-based HTTP server; token auth; ISO-8601 JSON envelope around ClaudeUsage; primary-LAN-address helper.
  • SharedDataStore.swift — persistence for enabled/port and secure token generation.
  • AppDelegate.swift — start on launch if enabled, stop on terminate.
  • MobileAppView.swift — real pairing screen (enable toggle, QR code with {host,port,token}, copy/regenerate token, security note).

Testing

  • Builds on Xcode 26.5.
  • Verified the endpoint with curl: 401 on missing/bad token, 404 on unknown path, and a correct ClaudeUsage JSON envelope on a valid request.
  • Verified end-to-end against the Expo companion app (fetch + parse + render).

Notes / follow-ups

  • New MobileAppView strings are currently English literals — happy to wire them into the localization system if you'd like before merging.
  • Enabling the server may trigger the macOS firewall "accept incoming connections" prompt on first run.

Adds LocalServerService: an opt-in, read-only HTTP server exposing the current
usage snapshot to trusted devices on the local network (e.g. a companion mobile
app). Disabled by default; every request requires a Bearer token; the Claude
session key never leaves the machine.

- LocalServerService.swift: NWListener HTTP server, GET /v1/usage + /v1/ping,
  token auth, serves DataStore.loadUsage() as JSON; LAN address helper.
- SharedDataStore: enabled/port/token persistence + token generation.
- AppDelegate: start on launch if enabled, stop on terminate.
- MobileAppView: real pairing screen (enable toggle + QR code + token) replacing
  the previous coming-soon placeholder.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant