> your AI agent picks dependencies from memory; give it dated facts — try starlog.dev ↗ vet your agent's deps ↗ vibe-coding is fine. vibe-importing isn’t. — try starlog.dev ↗ vibe-importing isn’t fine ↗ your agent has never seen your private packages — try starlog.dev ↗ facts for private packages ↗ a linter for the dependencies your AI agent picks — try starlog.dev ↗ a linter for agent deps ↗ whois is redacted, cdns mask the rest — get the real operator — try whoisgeni.us ↗ who really runs that domain ↗ domain attribution that shows its work — full evidence chain — try whoisgeni.us ↗ domain intel w/ evidence ↗

← Back to Articles

jean-claude: The HTTPS Proxy That Fixes What Anthropic Won't

[ View on GitHub ]

jean-claude: The HTTPS Proxy That Fixes What Anthropic Won't

Hook

When Claude Code silently overwrites your local settings on startup because Anthropic's managed API has the final word, you have two choices: accept it, or build a man-in-the-middle proxy. Etienne Pasteur chose option two.

Context

Most HTTP mocking tools assume you control the client. Libraries like Mock Service Worker let you intercept fetch() calls at the library layer. Tools like json-server give you a fake REST API to point your app at. But what happens when the client is a third-party CLI binary you can't modify, hitting an API you don't control, with no configuration flag to override the endpoint?

This is the problem jean-claude solves. It's not a general-purpose debugging proxy like mitmproxy or Charles—it's a surgical tool for developers trapped between an opinionated CLI and a managed API. The motivating use case is brutally specific: Claude Code reads settings from Anthropic's servers at startup, and there's no official way to freeze those values for local development. If you're iterating on a feature that depends on stable settings, or if you're offline, or if you simply want deterministic behavior, you're out of options. jean-claude gives you back control by intercepting HTTPS traffic, applying declarative YAML transformation rules, and returning whatever responses you define—all while maintaining the illusion that the real API is responding.

Technical Insight

Trust Bootstrap

HTTPS Request

Check cert cache

Per-hostname cert

signed by CA

Decrypt & Parse HTTP

Match: respond

Match: patch/redirect

TLS via bundle.pem

Trusts both proxy + internet

Response

Apply transforms

JSON Patch/merge

Client Application

jean-claude Proxy

Dynamic Certificate Generator

YAML Rule Engine

Upstream Server

Trust Bundle

System CAs + Custom CA

System architecture — auto-generated

The core technical challenge is trust. To intercept HTTPS, the proxy must terminate TLS connections by presenting a certificate the client accepts. jean-claude generates a Certificate Authority on first run, then dynamically creates per-hostname certificates signed by that CA for every domain it intercepts. The client trusts these certificates because the CA is injected into NODE_EXTRA_CA_CERTS. Simple enough—except for a trap that kills most implementations.

When you set NODE_EXTRA_CA_CERTS or SSL_CERT_FILE, Node replaces the system certificate store with your custom bundle. This breaks all legitimate HTTPS traffic because the client no longer trusts Let's Encrypt, DigiCert, or any other public CA. Most proxy tools punt on this: they tell you to manually merge your CA with system roots using platform-specific commands. jean-claude solves it automatically with a clever bundle construction:

const systemCAs = tls.getCACertificates('system');
const customCA = fs.readFileSync('./ca-cert.pem', 'utf-8');
const bundle = [...systemCAs, customCA].join('\n');
fs.writeFileSync('./bundle.pem', bundle);

The tls.getCACertificates('system') API—new in Node 22.21—reads platform-native trust stores (Windows Certificate Manager, macOS Keychain, Linux ca-certificates) and returns them as an array. By merging these with the proxy's CA, child processes trust both the proxy AND upstream servers. Environment variable setup becomes trivial: NODE_EXTRA_CA_CERTS=./bundle.pem. No manual steps, no platform-specific scripts, no broken HTTPS.

The rule engine is where jean-claude differentiates from heavyweight alternatives. Configuration lives in YAML files that are re-read on every request—no restart required. Rules match requests using path-to-regexp patterns and apply one of three actions: respond (return a static file), patch (apply RFC 6902 JSON Patch), or redirect (rewrite the URL before proxying upstream). Here's a practical example:

rules:
  - match: https://api.anthropic.com/v1/organizations/:org/settings
    respond: ./stubs/settings.json
  - match: https://api.example.com/users/:id
    respond: ./stubs/users/{id}.json
  - match: https://api.example.com/config
    patch:
      - op: replace
        path: /features/newUI
        value: false

The first rule intercepts Claude's settings API and returns a local file, giving you stable configuration. The second uses path interpolation—:id from the URL is substituted into the filename, so requesting /users/42 serves users/42.json. The third uses JSON Patch to surgically edit a single field in the upstream response without maintaining a full copy. This is more maintainable than wholesale mocking when APIs evolve frequently.

The CLI design reveals thoughtful UX decisions. Two execution modes handle different use cases: jean-claude run <command> wraps the target process, inheriting stdio so TUI apps render correctly. jean-claude start runs the proxy in the background, writing connection details to session.json, then you manually set environment variables—useful for GUI apps where process wrapping isn't viable. The tool detects existing proxies by reading HTTPS_PROXY and chains through them automatically, critical for corporate environments. Crucially, it deliberately unsets NO_PROXY rather than defaulting to localhost, because silently bypassing local traffic creates confusing failure modes.

One sophisticated detail: the recording mode. Run with --record, and every upstream response is captured to disk with auto-generated filenames. Combine this with --rules to set up capture targets, then edit the recorded files and flip them to respond mode. This workflow—intercept real traffic, capture responses, iterate on stubs—beats manually writing mock data from API docs.

Gotcha

Certificate pinning breaks everything. If your target CLI validates the server's certificate against a hardcoded public key (common in mobile SDKs and security-conscious tools), jean-claude's dynamically-generated certificates will be rejected with no workaround except the tlsPassthrough escape hatch—which defeats the entire purpose by letting traffic pass unmodified. You'll discover this only at runtime when requests fail with certificate errors.

The Node version requirement (≥22.21 or ≥24.5) is stricter than it appears. The tool depends on NODE_USE_ENV_PROXY, a flag that makes the native http module respect HTTPS_PROXY. Older Node versions silently ignore the proxy, and the CLI's version warning at startup doesn't help automated CI pipelines that might be running Node 20. Worse, if you're proxying an older version of Claude Code that bundles its own Node runtime, you're stuck unless you can patch the binary. The YAML rule engine is deliberately simple—no variables, no conditionals, no loops. You can't write rules like 'return error 500 for 10% of requests' or 'lock this endpoint after three calls.' Stateful mocking requires external orchestration or switching to mitmproxy's Python scripting.

Verdict

Use if: you're developing against a third-party API through a CLI tool you can't modify, you need hot-reloadable declarative rules instead of code, you want version-controlled reproducible mocking for your team, or you're specifically trying to stabilize Claude Code's settings. Skip if: your target uses certificate pinning (you'll get nothing but errors), you need stateful mocking or complex request transformation (the rule engine is too limited), you control the client source (mock at the HTTP library layer instead), you need to debug mobile/desktop GUI apps where environment injection is awkward, or you require Node <22.21 (the trust bundle construction will fail). This tool is hyper-focused: it solves the 'opinionated CLI hitting managed API' problem better than any alternative, but it's the wrong choice for general-purpose HTTP debugging.