Metadata-Version: 2.4
Name: halton-meter
Version: 0.1.6
Summary: Local LLM API proxy — captures usage, attributes cost to projects.
Project-URL: Homepage, https://meter.haltonlabs.com
Project-URL: Repository, https://github.com/haltonlabs/halton-meter
Project-URL: Issues, https://github.com/haltonlabs/halton-meter/issues
Project-URL: Changelog, https://github.com/haltonlabs/halton-meter/releases
Author-email: Halton Labs <hello@haltonlabs.com>
Maintainer-email: Halton Labs <hello@haltonlabs.com>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: anthropic,claude,cost-tracking,governance,llm,mitmproxy,observability,openai,proxy
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: System :: Networking :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: aiosqlite>=0.19
Requires-Dist: certifi>=2024.0
Requires-Dist: click>=8.1
Requires-Dist: fastapi>=0.110
Requires-Dist: greenlet>=3.0
Requires-Dist: httpx>=0.27
Requires-Dist: mitmproxy>=10.0
Requires-Dist: psutil>=5.9
Requires-Dist: pydantic>=2.0
Requires-Dist: rich>=13.0
Requires-Dist: sqlalchemy[asyncio]>=2.0
Requires-Dist: structlog>=24.0
Requires-Dist: tomli>=2.0; python_version < '3.11'
Requires-Dist: uvicorn>=0.27
Description-Content-Type: text/markdown

# Halton Meter

**Local LLM API proxy that captures usage and attributes cost to projects.**

Halton Meter is a governance and cost-attribution tool for LLM API spend. It runs a local proxy that observes outbound traffic to LLM providers (Anthropic, OpenAI, Gemini, Groq, etc.), logs every request with project-level attribution, computes accurate cost from published pricing, and surfaces it in a dashboard.

Designed for solo developers, agencies, and in-house dev teams who use Claude Code, the Anthropic SDK, or other LLM clients heavily and want transparency over what's being spent and on what.

## Quickstart

The exact happy-path sequence on a clean macOS machine:

```bash
pipx install halton-meter
halton-meter init                  # plug-and-play; macOS will prompt for admin password
halton-meter run claude            # meter Claude Code's API calls
halton-meter report                # see captured rows
```

That's it. `halton-meter init` installs the local CA cert (a macOS dialog
will pop up asking for your password), the launchd supervisor entry, and
the port allocations. By default, only commands you explicitly route via
`halton-meter run <cmd>` are metered — browsers, GUI apps, and Spotlight-
launched terminals are untouched, so `init` cannot break HSTS browsing.

Already used a previous version? `halton-meter doctor` prints a top-line
verdict (HEALTHY / INCONSISTENT / BROKEN) plus copy-pasteable next steps.

To extend coverage:

```bash
halton-meter init --apps           # cover terminals + Spotlight-spawned apps
                                   # (no system proxy — cannot break browsing)
halton-meter init --full           # cover everything: browsers, GUI apps,
                                   # terminals (system proxy + launchctl env)
```

(`--gui` is kept as a deprecated alias for `--full`.)

Requires Python 3.11+.

## What if `halton-meter init` doesn't succeed?

Most failures are caught by the post-init self-test, which rolls the
install back automatically and exits non-zero. Common cases:

| Symptom | Cause | Action |
|---|---|---|
| The macOS admin dialog appears, asks for your password | Expected — the cert-trust step needs admin rights | Type your account password and click OK. This is the same dialog Brew, the macOS installer, etc. use. |
| `--non-interactive cannot elevate ...` (exit 2) | Both osascript and pre-cached sudo are unavailable | Either run `halton-meter init` interactively (the GUI dialog will appear) OR pre-cache with `sudo -v && halton-meter init --non-interactive`. |
| Default port 8080 / 8765 is busy | Auto-fallback fires; daemon picks 8081 / 8766 | The success panel and `halton-meter status` show the actual port with `(fallback from busy default :8080/:8765)`. Configure your tool to target the actual port. |
| Cert is already trusted | Idempotent path | Init skips the trust step and proceeds. Re-runnable safely. |
| `halton-meter status` says BROKEN | Some component drifted | Run `halton-meter doctor` for a row-by-row diagnosis with concrete next-actions. Each row has a copy-paste fix. |
| Live daemon already running, plist needs refresh | `halton-meter init` will prompt before unloading | Confirm with `y` at the prompt to proceed (briefly drops the live API connection during the restart) or `n` to skip the supervisor refresh. |

Switching between modes is a single command — v0.1.5 reconciles all
mode-specific state in either direction (no `uninstall` step required):

```bash
halton-meter init                  # env-only (default)
halton-meter init --apps           # apps (env vars, no system proxy)
halton-meter init --full           # full (env vars + system proxy)
```

The reconciler covers all 9 cells of the (env-only, apps, full) ->
(env-only, apps, full) transition matrix. Any stale state from a prior
mode (system proxy, launchctl env, shell-rc block, proxy-managed
sentinel) is cleaned up to match the requested mode at the end of each
`init` run.

Full uninstall (preserves `db.sqlite` by default):

```bash
halton-meter uninstall                          # remove plists, restore proxy
halton-meter uninstall --purge                  # also delete config + sentinels
halton-meter uninstall --purge --include-logs   # also delete db.sqlite
```

## Three install modes

Halton Meter ships with **three install modes**, picked at `init` time:

| Mode | Invoke | What it sets up | What gets metered | Risk |
|---|---|---|---|---|
| **env-var-only** (default) | `halton-meter init` | CA cert + daemon + launchd plists only. Does **not** touch system proxy, launchctl env, or shell rc. | Whatever you explicitly route via `halton-meter run <command>` (Claude Code, the Anthropic SDK, `python my_script.py`, an interactive subshell with `--shell`, …). | Cannot break HSTS browsing — we never touch the system proxy. |
| **apps** *(NEW in v0.1.5)* | `halton-meter init --apps` | Above **plus** writes `HTTPS_PROXY` / `NODE_EXTRA_CA_CERTS` / friends into launchd's user domain (so Spotlight/Dock-spawned apps see them) **plus** appends an idempotent block to your shell rc (so new terminals inherit them). Does **not** enable the system proxy. | Spotlight/Dock-spawned apps that respect `HTTPS_PROXY` env (Claude Desktop, JetBrains IDEs, …) **and** new shells launched after the install. **Not** browsers. | Cannot break HSTS browsing — system proxy is still untouched. |
| **full** | `halton-meter init --full` | All of `apps` **plus** enables the macOS system proxy. | Browsers, GUI apps that ignore `HTTPS_PROXY` env, every other client. | May break HSTS browser sites if the CA isn't trusted by SecTrust. The daemon refuses to enable the proxy when cert verification fails (P0-1 in v0.1.3). |

`--gui` is preserved as a deprecated alias for `--full` (legacy
v0.1.3/v0.1.4 sentinel value `gui` is auto-migrated to `full` on next
`init` run).

### Decision tree

* "I just want to meter my Claude Code / SDK calls and not worry about
  anything else." -> **env-only** (default).
* "I want every app I open from Spotlight to be metered, but I don't
  want my browser to break if anything goes wrong with the cert."
  -> **--apps**.
* "I want to capture every HTTPS request my Mac makes, including
  browser traffic, and I'm OK with the cert-trust step being a hard
  prerequisite." -> **--full**.

### Witnessed-failure rationale (why three modes, not two)

v0.1.3 shipped just two — env-only and `--gui` — driven by two
witnessed regressions on Vikrant's MacBook 2026-04-30: (1) Claude Code
(Node.js) bypasses the macOS system proxy entirely, so a system-proxy-
only install captured zero traffic; (2) Chrome / SecTrust rejected the
proxied connection on totaljobs.com (HSTS) when the cert was in a
mid-trust state, breaking browsing. The `--apps` mode added in v0.1.5
captures the middle ground: terminal + Spotlight-app coverage WITHOUT
the system-proxy-can-break-browsing risk. Specifically, `--apps`
covers the Windsurf IDE / Claude Desktop / Cursor / JetBrains case
(Spotlight-spawned, Node-bypasses-system-proxy clients) without
touching the system proxy panel.

## Useful commands

```bash
halton-meter init [--apps|--full] [--no-shell-rc]  # install (re-runnable; idempotent)
halton-meter run <cmd> [args...]             # exec wrapper that injects metering env
halton-meter run --shell                     # interactive subshell with metering env
halton-meter status [--json]                 # mode-aware HEALTHY / INCONSISTENT / BROKEN
halton-meter doctor [--json] [--curl]        # one-command diagnostic; copy into a ticket
halton-meter start | stop                    # supervisor control
halton-meter uninstall [--purge] [--include-logs]
halton-meter reset-proxy                     # emergency: disable system proxy + reset
```

`halton-meter doctor` is the diagnostic of last resort: it prints every signal that matters (daemon health, cert trust, system proxy state, launchctl env, shell rc marker, env in current process) and ends with a top-line verdict and concrete next-actions. `--curl` adds an end-to-end TLS smoke test against `https://example.com` (hard-coded — never a provider domain).

## What it does

- Intercepts HTTPS traffic to LLM provider endpoints via a local mitmproxy instance
- Parses request/response bodies to extract model, tokens, cost
- Attributes each request to a project based on the calling process
- Stores everything locally in SQLite (`~/.halton-meter/db.sqlite`)
- Optionally syncs to a backend for dashboard visualisation

## What it doesn't do

- Doesn't wrap SDKs — your code stays exactly as it is
- Doesn't intercept anything you don't ask it to — only configured LLM endpoints
- Doesn't send your data anywhere by default — runs entirely locally
- Doesn't break your workflow — if the proxy fails, traffic falls through to the real provider

## Known limitations

Honest list, not a roadmap. Apps that ignore both the macOS system proxy AND `HTTPS_PROXY` env have no general capture path:

- **Go binaries with `GODEBUG=netdns=go`.** Go's pure-Go resolver path bypasses `HTTPS_PROXY` env in some builds. Calls reach the provider unmetered. Mitigation: launch the binary via `halton-meter run` (sets `HTTPS_PROXY` explicitly) — but if the binary opts out of proxy env, even that doesn't help.
- **libcurl callers using `--insecure` or with `CURLOPT_PROXY` not honoured.** Same root cause: the client doesn't consult either signal. `halton-meter run` will set `HTTPS_PROXY`, but `--insecure` callers skip TLS verification entirely.
- **WSL2 networking on Windows hosts.** v1.0 doesn't route WSL2 guest traffic through the Windows-side proxy. WSL2 + halton-meter is not yet supported. Linux-native and macOS are the supported platforms.
- **Hardcoded HTTP stacks that open raw TLS sockets to known provider IPs.** Anything that bypasses both system proxy AND `HTTPS_PROXY` env will reach the provider unmetered.
- **Claude Code (Node.js) requires `HTTPS_PROXY` env.** Node honours `HTTPS_PROXY`, not the macOS system proxy panel. In `--gui` mode the launchctl user-domain `setenv` covers Spotlight/Dock-spawned apps; for terminal-launched Claude Code, use `halton-meter run claude` (env-only mode) or open a new shell after the install (so the rc-appended export takes effect).
- **Late-coming network interfaces.** macOS network services are enumerated once when the daemon starts (cached for the process lifetime). If you plug in a new interface — Thunderbolt dock, USB-tethered iPhone — restart with `halton-meter stop && halton-meter start`.

## Project

Halton Meter is open source under Apache 2.0. Built by [Halton Labs](https://haltonlabs.com).

- Source: https://github.com/haltonlabs/halton-meter
- Issues: https://github.com/haltonlabs/halton-meter/issues
- Security: see `SECURITY.md` in the repository

This package is the daemon component. The dashboard lives in the same repository and is run separately.
