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_DISABLEDto a truthy value (1,true,yes,on). The nextopik-cipx synctears the install down — removes the launchd / systemd supervisor unit and clears the managedANTHROPIC_BASE_URL— so Claude Code routes straight to Anthropic. As a backstop,opik-cipx proxyexits 0 immediately while disabled. Disabling never breaks the Claude Code session. - Back on:
unset CIPX_DISABLED, thenopik-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'sANTHROPIC_BASE_URLat 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 orinstall.sh.logs/cipx.log— the gateway log (opik-cipx logstails 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—falseships 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 ownOPIK_*vars.OPIK_CIPX_DEBUGtoggles debug.CIPX_SENTRY(offdisables) andCIPX_SENTRY_DSN— anonymous error telemetry, opt-in via the DSN.
Invariants
- Loopback only. Proxy, admin, and viewer all bind
127.0.0.1and 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.