ccproxy is a transparent LLM API interceptor built on mitmproxy. It embeds mitmweb in-process, intercepts HTTP traffic from any LLM client, and feeds it through a configurable pipeline that can observe, rewrite, and re-route requests between providers. Two entry points serve different use cases: a reverse proxy for SDK clients and a WireGuard tunnel for full transparent capture of arbitrary processes.
ccproxy init # writes ~/.config/ccproxy/ccproxy.yaml
ccproxy init --force # overwrite existing configEdit ~/.config/ccproxy/ccproxy.yaml to configure providers, transform
overrides, and hooks. The config directory can be overridden with
--config PATH or the CCPROXY_CONFIG_DIR environment variable.
ccproxy startRuns in the foreground. The server binds two listeners:
- Reverse proxy on the configured port (default
4000) for SDK clients. - WireGuard UDP tunnel on an auto-assigned port for namespace-jailed processes.
The mitmweb UI URL (with auth token) is printed at startup. Use process-compose or systemd for background supervision.
ccproxy status # rich table: proxy, inspector, config, logs
ccproxy status --json # machine-readable JSON
ccproxy status --proxy # health check: exit 0 if proxy is up, 1 if down
ccproxy status --inspect # health check: exit 0 if inspector is up, 2 if downHealth check flags use a bitmask: --proxy --inspect exits 0 only if both are
healthy, 3 if both are down.
ccproxy logs # tail the daemon log file ($CCPROXY_CONFIG_DIR/ccproxy.log)
ccproxy logs -f # follow
ccproxy logs -n 50 # last 50 linesFor journal-routed logging (use_journal: true) read journalctl --user -t <identifier>
directly; for a process-compose-supervised dev instance use
process-compose process logs ccproxy.
Every flow enters ccproxy through one of two listeners. The entry point determines how the flow is treated by the pipeline.
SDK clients point their base URL at ccproxy:
ccproxy run -- my-tool # sets ANTHROPIC_BASE_URL, OPENAI_BASE_URL, OPENAI_API_BASEOr set the environment manually:
export ANTHROPIC_BASE_URL=http://127.0.0.1:4000
export OPENAI_BASE_URL=http://127.0.0.1:4000The client sends requests to ccproxy as if it were the provider.
Transform rules determine where the request actually goes.
Unmatched reverse proxy flows receive a 501 error — there is no default
upstream since the placeholder backend (localhost:1) is intentionally invalid.
For full transparent capture of all outbound traffic from a process:
ccproxy run --inspect -- claude --model haiku -p "hello"
ccproxy run -i -- aider --model claude-3-haikuThis creates a rootless Linux network namespace (no root required on Linux 5.6+ with unprivileged user namespaces enabled), routes all TCP/UDP traffic through a WireGuard tunnel into mitmproxy, and injects a combined CA bundle so TLS interception works transparently. The confined process has no direct internet access — everything exits through the WireGuard tunnel and passes through the full addon pipeline.
Unmatched WireGuard flows pass through to their original destination unchanged, so the subprocess works normally even for traffic that ccproxy has no transform rules for.
Requirements: ccproxy start must be running.
The following tools must be in PATH: slirp4netns, unshare, nsenter, ip,
wg, iptables, and sysctl. NixOS with kernel 6.18+ satisfies these by
default. On Windows, this path is supported only inside WSL2; use the
ccproxy.wsl artifact for the supported out-of-box environment.
| Reverse Proxy | WireGuard Namespace | |
|---|---|---|
| How traffic arrives | Client sets base_url to ccproxy |
All traffic captured transparently |
| Client modification | Requires base_url env var |
None — process is unaware of ccproxy |
| Unmatched flows | 501 error | Pass through unchanged |
| Shaping observation | Not observed (consumer of profiles) | Always observed (reference traffic) |
| Shaping application | Applied (when transform matched) | Not applied |
| TLS | Client connects via plain HTTP | mitmproxy intercepts and re-signs with its CA |
Every request passes through a fixed addon chain:
┌─────────────────────────┐
│ ReadySignal │ Startup synchronization
└────────────┬────────────┘
┌────────────▼────────────┐
│ InspectorAddon │ Flow capture, OTel spans, client request snapshot, SSE streaming
└────────────┬────────────┘
┌────────────▼────────────┐
│ FingerprintCaptureAddon │ TLS ClientHello capture (JA3/JA4 material for shapes)
└────────────┬────────────┘
┌────────────▼────────────┐
│ MultiHARSaver │ ccproxy.dump command (multi-page HAR export)
└────────────┬────────────┘
┌────────────▼────────────┐
│ ShapeCapturer │ ccproxy.shape command (validate + persist .mflow)
└────────────┬────────────┘
┌────────────▼────────────┐
│ Inbound Hooks │ Auth token injection, session ID extraction, Perplexity ingest
└────────────┬────────────┘
┌────────────▼────────────┐
│ Transform │ Route matching, provider dispatch (passthrough / redirect / transform)
└────────────┬────────────┘
┌────────────▼────────────┐
│ Outbound Hooks │ Gemini envelope wrap, Perplexity headers, MCP notification injection, verbose mode, shape replay, commitbee compat
└────────────┬────────────┘
┌────────────▼────────────┐
│ TransportOverrideAddon │ Reroute fingerprint-profile providers through the curl-cffi sidecar
└────────────┬────────────┘
┌────────────▼────────────┐
│ AuthAddon │ 401-detect → refresh → replay (for auth-injected flows)
└────────────┬────────────┘
┌────────────▼────────────┐
│ GeminiAddon │ Gemini capacity fallback + cloudcode-pa envelope unwrap
└────────────┬────────────┘
┌────────────▼────────────┐
│ PerplexityAddon │ Perplexity SSE patching + thread bookkeeping
└────────────┬────────────┘
┌────────────▼────────────┐
│ EgressSanitizerAddon │ Strip ccproxy-internal headers before egress
└────────────┬────────────┘
▼
Provider API
The first real addon in the chain. Before any hook touches the request, it captures a complete snapshot of the original client request (method, URL, headers, body). This snapshot is the ground truth of what the client sent and is used for:
- Shaping observation — learning what a reference client sends.
- Client Request content view — visible in the mitmweb UI under the "Client-Request" tab.
ccproxy flows compare— diffing what the client sent vs what the pipeline forwarded.- HAR export — each flow's HAR page includes both the forwarded and client request.
InspectorAddon also manages OTel span lifecycle and enables SSE streaming on
responses with content-type: text/event-stream.
Run before the transform stage. Default hooks:
inject_auth— Detects sentinel API keys (see OAuth) and substitutes real tokens from configured credential sources.extract_session_id— Parsesmetadata.user_idfrom the request body and stores the session ID for downstream hooks (MCP notification injection).extract_pplx_files— For Perplexity Pro traffic, uploadsimage_urlattachments through Perplexity's S3 batch chain and rewrites the body to reference the uploaded files.pplx_thread_inject— Resolves Perplexity Pro thread continuation (explicit body session_id, organic L1 cache hit, or pass-through).
Matches the request against inspector.transforms rules (first match wins) and
dispatches in one of three modes.
See Transform Rules.
Run after the transform stage. Default hooks:
gemini_cli— For Gemini sentinel-key traffic, wraps the body in thev1internalenvelope, conditionally masqueradesgoogle-genai-sdk/*UAs as the Gemini CLI, and rewrites the path tocloudcode-pa.googleapis.com.pplx_stamp_headers— For Perplexity Pro traffic, swaps Bearer auth for the browser-shape Cookie + UA + Origin + sec-fetch-* header bundle.pplx_preflight— Best-effortGET /search/new?q=...warm-up before a Perplexity Pro ask.inject_mcp_notifications— Drains buffered MCP terminal events for the current session and injects them as synthetic tool_use/tool_result message pairs before the final user message.verbose_mode— Stripsredact-thinking-*from theanthropic-betaheader to enable full thinking block output from Anthropic models.shape— Replays a captured shape ({provider}.mflow) onto reverse proxy and OAuth-injected flows: strips configured headers, injectscontent_fieldsfrom the incoming request, runs shape inner-DAG hooks (UUID regeneration, billing-header re-signing, cache breakpoint normalization), stamps the result onto the outbound flow. Only fires on flows that matched a transform/redirect rule.commitbee_compat— Last-mile compatibility shim for the commitbee tool — appends a raw-JSON instruction to its system prompt.
AuthAddon and GeminiAddon run after this stage as full mitmproxy addons
(not pipeline hooks): AuthAddon handles 401 detection / refresh / replay,
and GeminiAddon handles Gemini capacity fallback (sticky retry on 429/503
plus walking gemini_capacity.fallback_models) and cloudcode-pa envelope
unwrapping for streaming and buffered responses.
Hooks declare data dependencies (reads and writes) and are sorted into a DAG
via topological sort.
Hooks that don't depend on each other can run in parallel.
Errors in one hook don't block others — the sole exception is
AuthConfigError, which is fatal and propagates through the pipeline.
Hooks can be configured per-request via the x-ccproxy-hooks header:
x-ccproxy-hooks: +extra_hook,-verbose_mode
+ force-runs a hook, - force-skips it.
Transform rules — TransformOverride entries under inspector.transforms —
are an optional override layer on top of sentinel-driven Provider routing.
The default list is empty; most routing comes from providers via
inject_auth's sentinel detection. Override rules cover edge cases:
forcing a specific destination for a path/model/host combination, bypassing
auth for a specific host, etc.
Rules are evaluated in order; first match wins.
All match fields are optional regexes and combined with AND logic:
match_host— regex matched against the request's host,Hostheader, andX-Forwarded-Host.match_path— regex matched against the URL path (default.*matches everything).match_model— regex matched againstglom(body, "model")from the JSON request body.
passthrough — Forward to the original destination unchanged.
The request is observed (logged, traced) but not modified.
Useful for WireGuard reference traffic that should flow through transparently.
inspector:
transforms:
- action: passthrough
match_host: cloudcode-pa\.googleapis\.com$redirect — Rewrite the destination host/port/scheme/path and inject auth
credentials, but preserve the request body format.
For same-format routing where the body is already correct.
Auth resolves via dest_provider → providers[name].
inspector:
transforms:
- action: redirect
match_path: ^/v1internal
dest_provider: geminitransform — Full cross-provider rewrite via lightllm.
Changes the destination URL and rewrites the entire request body from one API
format to another (e.g. OpenAI format to Anthropic format).
The response is also transformed back to the client's expected format.
inspector:
transforms:
- action: transform
match_path: ^/v1/chat/completions
match_model: ^gpt-4o
dest_provider: anthropic
dest_model: claude-haiku-4-5-20251001| Field | Actions | Purpose |
|---|---|---|
action |
all | passthrough, redirect, or transform (default: redirect) |
match_host |
all | Hostname regex (optional) |
match_path |
all | Path regex (default: .*) |
match_model |
all | Model regex (optional) |
dest_provider |
redirect, transform | Provider name in providers — resolves host/path/auth/format |
dest_model |
transform | Destination model name |
dest_host |
redirect | Raw host override (bypasses Provider lookup) |
dest_path |
redirect | Raw path override |
dest_vertex_project |
transform | GCP project ID (Vertex AI) |
dest_vertex_location |
transform | GCP region (Vertex AI) |
- Non-streaming responses with a matched transform rule are converted back to OpenAI format before being sent to the client.
- SSE streaming responses use an
SSETransformerthat parses SSE events from the upstream provider and re-serializes them as OpenAI-format SSE chunks in real time. - Passthrough and redirect responses are forwarded unchanged.
ccproxy uses sentinel API keys to trigger automatic token substitution. A sentinel key is a special value that signals ccproxy to look up the real credential from a configured source.
sk-ant-oat-ccproxy-{provider}
For example, sk-ant-oat-ccproxy-anthropic tells the inject_auth hook to
resolve the real token from providers.anthropic.auth.
providers:
anthropic:
auth:
type: command
command: "cat ~/.anthropic/oauth_token"
host: api.anthropic.com
path: /v1/messages
type: anthropic
gemini:
auth:
type: file
file: "~/.config/gemini/oauth_token"
host: cloudcode-pa.googleapis.com
path: "/v1internal:{action}"
type: gemini
openai:
auth:
type: command
command: "op read 'op://vault/openai/api_key'"
header: "authorization"
host: api.openai.com
path: /v1/chat/completions
type: openaiEach auth block is a discriminated AnyAuthSource — command, file,
anthropic_oauth, or google_oauth. A bare YAML string under auth:
auto-coerces to a command source.
Optional auth.header overrides the target header name (default:
authorization with Bearer prefix; set to x-api-key for raw injection).
When a response returns 401 and the request used an injected token
(metadata_from_flow(flow).auth_injected), AuthAddon.response() calls
config.resolve_auth_token(provider) to re-resolve the credential source.
For OAuth-source providers (anthropic_oauth, google_oauth) this triggers
another in-process refresh attempt; for static command / file loaders it
just re-reads the source. The request is then replayed with whatever token
the resolver returns; if the resolver yields nothing (empty token, refresh
failed), the 401 propagates to the client.
Some providers (Anthropic in particular) enforce client identity via headers, beta flags, system prompt prefixes, and signed billing headers. When ccproxy receives an SDK call lacking those markers, the request is structurally valid but will be rejected with 401/400.
A shape is a captured mitmproxy.http.HTTPFlow (a real, known-good request
from the target SDK) persisted as a {provider}.mflow file. At runtime, the
shape outbound hook replays the shape: configured headers are stripped,
content_fields from the incoming request are injected per the provider's
merge_strategies, shape inner-DAG hooks run (regenerating UUIDs, signing
the Anthropic billing header, normalizing cache_control breakpoints), and the
final shape is stamped onto the outbound flow.
Capture or refresh a shape any time the target CLI version changes:
ccproxy run --inspect -- claude -p "shape capture"
ccproxy shapes save anthropicdocs/shaping.md is the full reference: capture workflow,
storage layout, the inject/strip/shape-hooks pipeline, the cache breakpoint
hooks, the Anthropic billing salt configuration, custom shape hooks.
The inspector UI is available at the URL printed at startup (includes an auth token). It provides the standard mitmproxy flow list with two additions:
- Client-Request content view — a tab showing the pre-pipeline request snapshot (what the client originally sent, before any hooks or transforms modified it).
ccproxy.clientrequestcommand — returns the client request snapshot as structured JSON.
All subcommands accept repeatable --jq FILTER flags.
Each filter is a jq expression that consumes and produces a JSON array.
Filters chain with |. Default filters from flows.default_jq_filters config
are applied first.
# List recent flows
ccproxy flows list
ccproxy flows list --json
# Filter to Anthropic traffic
ccproxy flows list --jq 'map(select(.request.host | endswith("anthropic.com")))'
# Export HAR (opens in Chrome DevTools, Charles, Fiddler)
ccproxy flows dump > all.har
# Diff consecutive request bodies (sliding window)
ccproxy flows diff
# Compare client request vs forwarded request per flow
ccproxy flows compare
# Clear flows
ccproxy flows clear # clear filtered set
ccproxy flows clear --all # clear everythingccproxy flows dump produces a multi-page HAR 1.2 file.
Each flow becomes one page with two entries:
- Entry 0 (even index): the forwarded request and response — what was actually sent to the provider.
- Entry 1 (odd index): the client request (reconstructed from the pre-pipeline snapshot) paired with the same response.
This lets you compare what the client sent vs what the pipeline forwarded in any HAR viewer.
Configure persistent filters in ccproxy.yaml:
flows:
default_jq_filters:
- 'map(select(.request.host | endswith("anthropic.com")))'The daemon hosts a FastMCP streamable-HTTP server (flow inspection, shape
capture, conversation grouping, model catalog, Perplexity tools). MCP clients
connect to http://127.0.0.1:4030/mcp (the mcp.http bind) or to
/mcp on the proxy port itself — the proxy listener forwards it to the same
in-process server. Bearer auth is configured at mcp.http.auth.
The proxy listener also exposes POST /mcp/notify, which accepts MCP terminal
events fire-and-forget (no auth, always answers 200):
curl -X POST http://127.0.0.1:4000/mcp/notify \
-H 'Content-Type: application/json' \
-d '{"task_id": "...", "session_id": "...", "event": {...}}'Events are buffered per task (default max 65536 events, FIFO drop on
overflow, 600s TTL — see mcp.buffer in
docs/configuration.md). The
inject_mcp_notifications outbound hook drains the buffer for the current
session and injects events as synthetic tool_use/tool_result pairs before the
final user message in the conversation.
This allows external MCP servers to surface information into the LLM's context
window.
ccproxy exports keylogs for full packet capture decryption.
At startup, ccproxy writes:
{config_dir}/tls.keylog— TLS master secrets for all intercepted connections (inner TLS to provider APIs).{config_dir}/wg.keylog— WireGuard static private keys for the outer UDP tunnel.
# Capture traffic
sudo tcpdump -i any -w capture.pcap
# Open in Wireshark, then:
# 1. Decrypt WireGuard: Edit -> Preferences -> Protocols -> WireGuard -> Key log file -> wg.keylog
# 2. Decrypt TLS: Edit -> Preferences -> Protocols -> TLS -> (Pre)-Master-Secret log -> tls.keylogWith both keylogs loaded, the entire traffic path is visible: outer WireGuard UDP packets, inner TLS handshakes, and plaintext HTTP request/response bodies.
ccproxy emits OTel spans for every intercepted flow. Three modes with graceful degradation:
| Mode | Condition | Behavior |
|---|---|---|
| Real OTLP export | otel.enabled: true + packages installed |
Spans exported via gRPC |
| No-op tracer | enabled: false + API packages present |
Zero overhead |
| Stub | OTel packages absent | No imports, zero overhead |
otel:
enabled: true
endpoint: "http://localhost:4317"
service_name: "ccproxy"Each span includes HTTP semantics (http.request.method, url.full,
server.address), ccproxy-specific attributes (ccproxy.proxy_direction,
ctx.metadata.session_id), and GenAI semantic conventions (gen_ai.system,
gen_ai.operation.name) for flows to known provider hosts.
The Jaeger container in compose.yaml accepts OTLP gRPC on port 4317 and serves
the trace UI on port 16686.
The namespace jail creates a fully isolated network environment routed through mitmproxy. No root privileges are required.
┌─ Confined process ─────────────────────────────────┐
│ │
│ wg0: 10.0.0.1/32 default route → wg0 │
│ tap0: 10.0.2.100/24 gateway → 10.0.2.2 │
│ DNS → 10.0.2.3 │
│ │
└──────────────────┬──────────────────────────────────┘
│ WireGuard UDP
│ Endpoint: 10.0.2.2:{wg_port}
▼
┌─ slirp4netns NAT ──────────────────────────────────┐
│ 10.0.2.2 (gateway) ──────▶ host network │
└──────────────────┬──────────────────────────────────┘
│
▼
┌─ mitmweb WireGuard listener ───────────────────────┐
│ Decrypts tunnel → feeds into addon chain │
└────────────────────────────────────────────────────┘
| Address | Role |
|---|---|
10.0.0.1/32 |
WireGuard client interface (wg0) |
10.0.2.100/24 |
Namespace TAP interface (tap0) |
10.0.2.2 |
Host gateway (slirp4netns NAT) — WireGuard endpoint |
10.0.2.3 |
DNS forwarder (libslirp built-in) |
A background thread polls the namespace's /proc/{pid}/net/tcp every 0.5
seconds and dynamically forwards new listening ports via the slirp4netns API.
This allows tools that start local servers (e.g. OAuth callback listeners) to
receive connections from the host.
Inside the namespace, 127.0.0.1 is isolated loopback — host services are not
reachable there. iptables DNAT rules transparently redirect namespace localhost
traffic to the slirp4netns gateway (10.0.2.2), so tools with hardcoded
127.0.0.1 base URLs work without modification.
When the running ccproxy port differs from the default (4000), a port remap rule
handles the translation.
ccproxy run --inspect builds a combined CA bundle (mitmproxy's CA + system
CAs) and injects it into the subprocess environment via:
SSL_CERT_FILE = <combined bundle>
REQUESTS_CA_BUNDLE = <combined bundle>
CURL_CA_BUNDLE = <combined bundle>
NODE_EXTRA_CA_CERTS = <combined bundle>
This covers Python (ssl, urllib3, httpx, requests), curl, and Node.js
clients.
| Requirement | Check |
|---|---|
| Unprivileged user namespaces | /proc/sys/kernel/unprivileged_userns_clone == 1 |
slirp4netns |
In PATH |
unshare |
In PATH |
nsenter |
In PATH |
ip |
In PATH |
wg |
In PATH |
iptables |
In PATH |
sysctl |
In PATH |
Config file: $CCPROXY_CONFIG_DIR/ccproxy.yaml (default:
~/.config/ccproxy/ccproxy.yaml). Individual fields can be overridden via CCPROXY_
prefixed environment variables.
| Field | Default | Description |
|---|---|---|
host |
127.0.0.1 |
Bind address |
port |
4000 |
Reverse proxy listener port |
log_level |
INFO |
Root logger level (CCPROXY_LOG_LEVEL env var overrides) |
log_file |
ccproxy.log |
Daemon log file (relative to config dir; null disables) |
provider_timeout |
null |
Timeout (seconds) for ccproxy's own outbound httpx calls (OAuth refresh, 401 retry). null = no enforced timeout. |
use_journal |
false |
Route daemon logs to systemd journal |
journal_identifier |
derived from config-dir basename | SYSLOG_IDENTIFIER for the journal handler |
The startup readiness probe is configured at inspector.readiness.url
(default https://1.1.1.1/) and inspector.readiness.timeout_seconds
(default 5.0). Set inspector.readiness.url to null to skip the probe.
| Field | Default | Description |
|---|---|---|
port |
8083 |
mitmweb UI port |
cert_dir |
null |
mitmproxy CA certificate store (default: ~/.mitmproxy) |
provider_map |
(see below) | Hostname to OTel gen_ai.system mapping |
transforms |
[] |
Transform rules (see Transform Rules) |
mitmproxy |
(object) | mitmproxy option overrides |
Default provider_map:
provider_map:
api.anthropic.com: anthropic
api.openai.com: openai
generativelanguage.googleapis.com: google
openrouter.ai: openrouter| Field | Default | Description |
|---|---|---|
ssl_insecure |
true |
Skip upstream TLS verification |
stream_large_bodies |
null |
Stream threshold (null disables; otherwise 512k, 1m, 10m) |
body_size_limit |
null |
Hard body size limit (null = unlimited) |
web_host |
127.0.0.1 |
mitmweb UI bind address |
web_password |
null |
UI password (string, or {command:} / {file:} source). null generates a random token on each startup. |
web_open_browser |
false |
Auto-open browser on start |
ignore_hosts |
[] |
Regex patterns for hosts to bypass |
allow_hosts |
[] |
Regex patterns for hosts to intercept (exclusive) |
termlog_verbosity |
warn |
mitmproxy terminal log level |
flow_detail |
0 |
Flow output verbosity (0-4) |
The CA certificate store directory is set at inspector.cert_dir (a sibling
of inspector.mitmproxy), not inside this block.
providers:
anthropic:
auth:
type: command
command: "cat ~/.anthropic/oauth_token"
host: api.anthropic.com
path: /v1/messages
type: anthropic
deepseek:
auth:
type: command
command: "printenv DEEPSEEK_API_KEY"
header: x-api-key
host: api.deepseek.com
path: /anthropic/v1/messages
type: anthropicPer-entry fields:
auth— discriminatedAnyAuthSource(command/file/anthropic_oauth/google_oauth). A bare string auto-coerces to acommandsource. Optionalauth.headeroverrides the target auth header name.host— single destination hostname.path— destination path. Supports{model}and{action}templating.type— adapter-family name (anthropic,openai,google,gemini,vertex_ai,vertex_ai_beta,perplexity_pro) driving wire-format dispatch. Anthropic-compatible forks like DeepSeek and Z.AI usetype: anthropic.fingerprint_profile— optional curl-cffi browser impersonation profile (e.g.chrome131); routes the provider through the TLS fingerprint sidecar.
hooks:
inbound:
- ccproxy.hooks.inject_auth
- ccproxy.hooks.extract_session_id
- ccproxy.hooks.extract_pplx_files
- ccproxy.hooks.pplx_thread_inject
outbound:
- ccproxy.hooks.gemini_cli
- ccproxy.hooks.pplx_stamp_headers
- ccproxy.hooks.pplx_preflight
- ccproxy.hooks.inject_mcp_notifications
- ccproxy.hooks.verbose_mode
- ccproxy.hooks.commitbee_compat
- ccproxy.hooks.shapeEntries can also be {hook, params} dicts. The same form works for the shape
inner-DAG hooks under shaping.providers.{name}.shape_hooks:
shaping:
providers:
anthropic:
shape_hooks:
- ccproxy.shaping.regenerate
- hook: ccproxy.shaping.caching.strip
params:
paths: ["system.*.cache_control"]| Field | Default | Description |
|---|---|---|
enabled |
false |
Enable OTLP span export |
endpoint |
http://localhost:4317 |
OTLP gRPC endpoint |
service_name |
ccproxy |
OTel resource service name |
| Field | Default | Description |
|---|---|---|
enabled |
true |
Master switch for shape storage and application |
shapes_dir |
~/.config/ccproxy/shapes |
Directory holding per-provider {provider}.mflow shape files and patch series |
providers |
{} |
Per-provider shaping profiles (content_fields, merge_strategies, shape_hooks, preserve_headers, strip_headers, capture.path_pattern, optional billing for Anthropic) — see docs/shaping.md |
| Field | Default | Description |
|---|---|---|
default_jq_filters |
[] |
jq filters pre-applied to all ccproxy flows commands |
ccproxy start Start inspector server (foreground)
ccproxy init [--force] Initialize config files
ccproxy run [--inspect] -- <command> [args...] Run command with proxy environment
ccproxy status [--json] [--proxy] [--inspect] Show status / health check
ccproxy namespace status [--json] Show namespace runtime inputs
ccproxy namespace doctor [--json] Probe namespace DNS/egress/localhost
ccproxy logs [-f] [-n N] View logs
ccproxy flows list [--json] [--jq FILTER]... List flows
ccproxy flows dump [--jq FILTER]... Export multi-page HAR
ccproxy flows diff [--jq FILTER]... Sliding-window diff across flows
ccproxy flows compare [--jq FILTER]... Per-flow client-vs-forwarded diff
ccproxy flows clear [--all] [--jq FILTER]... Clear flows
Global options (before any subcommand):
--config PATH— override config directory-v/--verbose— show INFO/DEBUG output on CLI commands
The quickest end-to-end verification:
ccproxy start & # or via process-compose / systemd
ccproxy run --inspect -- claude --model haiku -p "what's 2+2"This exercises: namespace creation, WireGuard tunnel, TLS interception, the full hook pipeline, transform dispatch, upstream provider call, and SSE streaming back to the client.