# Opik Cipx

> How the opik-cipx cost-tracking proxy works — architecture, the CLI, where it stores state, how it's enabled and disabled, privacy and telemetry, and how to read `opik-cipx status`. Use whenever the user asks about opik-cipx, token/cost tracking, or Opik spans, or when spans stop reaching Opik, Claude Code seems to bypass the proxy, or you need to explain, diagnose, or repair any part of it.

- Skill: `comet-ml/opik-cipx` (Agent Skill)
- Install (CLI): `npx skillmds@latest add comet-ml/opik-cipx`
- Raw SKILL.md: https://api.skillmd.com/api/skills/comet-ml/opik-cipx/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: comet-ml (https://skillmd.com/u/comet-ml)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/comet-ml/opik-cipx

---


# opik-cipx

opik-cipx is a local reverse HTTP proxy between Claude Code and the Anthropic
API. Claude Code is pointed at it through `ANTHROPIC_BASE_URL`; opik-cipx owns
the TLS leg to `api.anthropic.com`. It captures every call, attributes the
token bill to cost buckets, and ships per-call spans to Opik. It is a single Go
binary — both the long-lived gateway and the short-lived commands below.

## How a session gets wired

Installing the plugin is the whole opt-in. Each Claude Code **SessionStart**
runs the plugin's hook launcher, which locates the binary and execs
`opik-cipx sync` (the hook event argument is ignored — `sync` is the same
operation every time). From then on every API call flows through opik-cipx.
There is no per-project or per-repo toggle and no marker file.

Wire path: Claude Code → `http://127.0.0.1:9909` (plain HTTP, loopback) →
opik-cipx tees the request and response → forwards over TLS to Anthropic. The
proxy only tees bytes; categorization and span-building run on a background
materializer so they never block the wire.

## Turning capture on and off

- **On:** install the plugin, or run `opik-cipx sync`. It is on by virtue of
  being installed.
- **Off:** set `CIPX_DISABLED` to a truthy value (`1`, `true`, `yes`, `on`).
  The next `opik-cipx sync` tears the install down — removes the launchd /
  systemd supervisor unit and clears the managed `ANTHROPIC_BASE_URL` — so
  Claude Code routes straight to Anthropic. As a backstop, `opik-cipx proxy`
  exits 0 immediately while disabled. Disabling never breaks the Claude Code
  session.
- **Back on:** `unset CIPX_DISABLED`, then `opik-cipx sync` (or restart Claude
  Code). Settings take effect only on (re)start — there is no hot-reload.

## CLI

All commands are subcommands of the single `opik-cipx` binary. Only
`opik-cipx proxy` is long-lived; the rest are short-lived helpers.

- `opik-cipx status [--json]` — snapshot the running daemon (read-only).
- `opik-cipx sync` — the SessionStart entry point: install/refresh the OS
  supervisor, bring the daemon up if it's down, and point Claude Code's
  `ANTHROPIC_BASE_URL` at the proxy. Idempotent; safe to run anytime.
- `opik-cipx restart` — force the daemon to restart and pick up the latest
  binary on disk.
- `opik-cipx logs` — print and tail the gateway log.
- `opik-cipx viewer [--open]` — print (and with `--open`, launch) the debug-UI
  URL.
- `opik-cipx purge` — stop the daemon and wipe the WAL spool. **Destructive:**
  drops any spans not yet shipped to Opik.
- `opik-cipx uninstall` — stop the daemon, remove the supervisor unit, and
  delete `~/.opik-cipx`. **Destructive.**
- `opik-cipx proxy` — run the gateway in the foreground (normally launched by
  the supervisor, not by hand).
- `opik-cipx replay` — re-derive spans from stored raw captures (offline,
  read-only).

`sync` and `restart` are the safe, idempotent repairs — non-destructive and
fine to run on your own. `purge` and `uninstall` destroy state; confirm with
the user before running either.

## Where state lives

Everything is under `~/.opik-cipx/` (override the root with `$CIPX_HOME`),
created at mode 0700:

- `bin/` — the binary, when installed via the plugin or `install.sh`.
- `logs/cipx.log` — the gateway log (`opik-cipx logs` tails it).
- `config.toml` — optional config file (override its path with `$CIPX_CONFIG`).
- `ports.json` — assigned loopback ports, auto-bootstrapped on first start.
- `state.json` — daemon bring-up handshake (pid, started_at).
- `spool/` — the write-ahead log that buffers spans; it survives Opik outages
  and drains when Opik is reachable again.
- `sessions/<session_id>.json` — per-session identity and repo metadata the
  SessionStart hook writes, joined onto spans at materialization.

## Reading `opik-cipx status`

`status` reads `state.json`, then GETs `/admin/stats` on the admin loopback.
Exit 0 = up, or not running (informational); exit 1 = `state.json` claims a pid
but `/admin/stats` was unreachable (daemon wedged).

**Header** — `running (pid N)`, the listener lines (`claude_code`, `admin`, and
`viewer`, all on `127.0.0.1`), `uptime`, `project`. `not running` → the daemon
is down; run `opik-cipx sync`.

**queue** — `in-memory`, `ready`, `inflight`, `wal hot` (bytes buffered),
`last sync` (age). `wal hot` climbing into hundreds of MiB, or `last sync`
minutes stale → the shipper is stuck; read `opik-cipx logs` for errors against
the Opik URL. A `degraded true` / `last-error` line means the WAL hit
disk-write failures.

**counters** — healthy progress shows in `requests_captured`,
`events_materialized`, `spans_shipped`. Flag any of these nonzero:
`materializer_errors_total`, `raw_captures_dropped`,
`requests_dropped_queue_full`, `spans_dropped_term`, `unknown_endpoints` (a
route cipx doesn't recognize — often a Claude Code wire-format change ahead of
an opik-cipx update), `panics_recovered` (a bug — surface
`~/.opik-cipx/logs/cipx.log`).

**materializer** — `lag_events`, `inflight_raw`, `ready_raw`; lag in the
thousands that won't drain = materializer wedged → `opik-cipx restart`.

**telemetry** — `off (no CIPX_SENTRY_DSN)` is the default. It reads `on` only
when `CIPX_SENTRY_DSN` is set and `CIPX_SENTRY` is not disabled.

`--json` exposes the raw `/admin/stats`. The hard signals when you need
precision: `stats.wal.degraded`, `stats.wal.last_write_error`, and
`stats.wal.dropped_events_inmem` / `dropped_events_disk`.

## Configuration

Resolution precedence is env var → `~/.opik-cipx/config.toml` → built-in
default, and changes need a daemon (re)start. Env vars the binary actually
reads:

- `CIPX_DISABLED` — master kill-switch (see above).
- `CIPX_CAPTURE_CONTENT` — `false` ships counts, costs, and structure but never
  prompt/completion bytes (redacted mode). Capturing content is the default.
- `CIPX_HOME` / `CIPX_CONFIG` — override the state root / config-file path.
- `CIPX_UPSTREAM_PROXY` — forward outbound traffic through another proxy.
- `OPIK_CIPX_BASE_URL` / `OPIK_CIPX_API_KEY` / `OPIK_CIPX_WORKSPACE` /
  `OPIK_CIPX_PROJECT` — the Opik destination, namespaced so they don't collide
  with the Opik SDK's own `OPIK_*` vars. `OPIK_CIPX_DEBUG` toggles debug.
- `CIPX_SENTRY` (`off` disables) and `CIPX_SENTRY_DSN` — anonymous error
  telemetry, opt-in via the DSN.

## Invariants

- **Loopback only.** Proxy, admin, and viewer all bind `127.0.0.1` and refuse
  anything else at startup. If something suggests otherwise, that's a red flag,
  not a thing to "fix".
- **Totals exact, categories proportional.** Token totals come straight from
  Anthropic's `response.usage`; per-bucket numbers are chars-proportional
  estimates. No tokenizer is ever called.
- **No bodies in logs or telemetry.** Auth headers are redacted at capture and
  Sentry events are scrubbed. Quote error strings, never payloads.

## Diagnosing

Run `opik-cipx status` first, then `opik-cipx logs`. `sync` and `restart` fix
most daemon problems and are safe to run yourself; never `purge` or `uninstall`
without asking. If the binary is missing entirely, the fix is reinstalling the
plugin (`/plugin install opik-cipx@opik-enterprise`) or re-running
`install.sh` — the daemon can't self-repair a missing binary.

