Between v3.22 and v3.28, dario's Claude backend closed six axes along which a proxy can diverge from real Claude Code. Each is a separate knob, each ships with its own test suite, each is surfaced through dario doctor where the axis has something to report. Defaults are chosen so existing setups don't regress.
| Axis | Release | What it does | How to tune |
|---|---|---|---|
| Request body key order | v3.22 | Top-level JSON key order of the outbound /v1/messages body is captured from CC's wire serialization and replayed byte-for-byte. Schema bumped v2 → v3; stale caches quarantined. |
Automatic once a live capture exists. The baked fallback carries a v2.1.112 snapshot. |
| Runtime / TLS ClientHello | v3.23 | Classifies the runtime as bun-match / bun-ja3-unverified / bun-bypassed / node-only and surfaces the class + hint in dario doctor. Bun yields the BoringSSL ClientHello CC presents; Node yields OpenSSL's (distinct JA3). Being on Bun is necessary but not sufficient — only Bun ≥ v1.3.14 is measured to reproduce CC's JA3, so an older Bun is flagged bun-ja3-unverified rather than green (#813). |
--strict-tls (or DARIO_STRICT_TLS=1) refuses to start proxy mode unless bun-match. DARIO_QUIET_TLS=1 silences the startup banner in known-fine environments. |
| Inter-request timing | v3.24 | Replaces the hardcoded 500 ms floor with a configurable floor + uniform jitter. A fixed 500 ms minimum-inter-arrival is an observable edge at scale; jitter dissolves the edge. | --pace-min=MS, --pace-jitter=MS, or DARIO_PACE_MIN_MS / DARIO_PACE_JITTER_MS. Legacy DARIO_MIN_INTERVAL_MS still honored. |
| Stream-consumption shape | v3.25 | When a downstream client disconnects mid-stream, CC keeps reading SSE to EOF. Dario now offers the same: drain upstream to completion even when the consumer has left. Default off — don't silently burn tokens. | --drain-on-close / DARIO_DRAIN_ON_CLOSE=1. Bounded by the existing 5-minute upstream timeout. |
| Session-ID lifecycle | v3.28 | Generalizes the v3.19 hardcoded 15-minute idle rotation into a tunable SessionRegistry with jitter, max-age, and per-client bucketing. Fixes a v3.27 body/header rotation race as a side effect. |
--session-idle-rotate=MS (default 900000), --session-rotate-jitter=MS, --session-max-age=MS, --session-per-client. Env mirrors DARIO_SESSION_*. Defaults are bit-identical to v3.27. |
| MCP / sub-agent reach | v3.26 + v3.27 | Not a wire axis — a surface axis. CC-aware tools can now address dario directly (sub-agent from inside CC, MCP server for any MCP client), so operators don't have to switch terminals to introspect the proxy. Read-only by design. | dario subagent install / dario mcp. See mcp-server.md and sub-agent.md. |
| Client identity headers | v5.4.19 | On the passthrough path isGenuineCCClient has already established the caller is Claude Code, so its own identity headers (user-agent, x-app, x-stainless-*, x-claude-code-*, x-client-*) are forwarded unchanged instead of being replaced with template values. The template exists to synthesise CC's shape for clients that are not CC; where the genuine article is in hand, forwarding beats imitating. Measured before the change: of 13 identity headers a real client sent, 12 were replaced and 1 dropped — 0 forwarded (#885). Auth, session id and the merged beta set stay dario's. | Automatic on the genuine-CC path; nothing to tune. Non-CC clients are unaffected, and the gate reads the request body, so headers cannot self-authorise. |
The original six-direction roadmap is complete; the axes below it are later findings. Note that header order is deliberately absent as an axis: orderHeadersForOutbound builds the captured sequence, but fetch() re-normalises it before the wire, and on Bun you cannot have both CC's JA3 and raw header control in one process (#813). The honest framing of the whole table is byte-identical body, structurally close headers — not packet-identical.