Ctrl K
ComunidadGuías…

config.example.yaml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Configuration template using the v8 layout.
# Copy this file to config.yaml and replace the client keys and any enabled examples.
# Optional upstream credentials and request rules remain commented until configured.
# Existing legacy configuration files and /v0/management requests remain supported.
# When both spellings of a field are present, the v8 value wins (including false,
# 0, and empty collections), and the corresponding legacy field is removed on load/save.
# Legacy-only fields still work. GET requests and config-version: 8 alone do not
# migrate a legacy file; a successful configuration write through /v8/management does.
# The compatibility notes at the end show old spellings for existing deployments.
config-version: 8

# Optional model catalog sources: http(s) URLs or absolute local file paths.
# Each explicit source overrides --local-model; otherwise --local-model uses
# embedded catalogs, and normal mode uses the official remote sources.
# Failed refreshes retain the last valid catalog; custom sources never fall back
# to official URLs. Files and URLs are refreshed every 3 hours and on source changes.
# Home mode never loads the general catalog, even when explicitly configured.
models:
  catalog: ""
  codex-catalog: ""
  devin-catalog: ""

# Listener, TLS, network discovery, and server operation.
server:
  # Server host/interface to bind to. Default is empty ("") to bind all interfaces (IPv4 + IPv6).
  # Use "127.0.0.1" or "localhost" to restrict access to local machine only.
  host: ""

  # Server port
  port: 8317

  # Maximum allowed size (in bytes) for incoming HTTP request bodies.
  # Requests exceeding this limit are rejected with HTTP 413 Payload Too Large.
  # Default is 0 (disabled / no limit).
  max-request-body-bytes: 0

  # Negotiated compression of downstream HTTP responses (including SSE streams).
  # Responses are compressed only when the client advertises zstd or gzip in
  # Accept-Encoding; WebSocket upgrades are never wrapped. Encoder memory is
  # retained for the lifetime of an open stream and depends on prior pool usage.
  response-compression:
    # Default: true.
    enabled: true
    # Non-streaming responses smaller than this many bytes are sent uncompressed.
    min-bytes: 1024

  # Global token for GitHub requests (for example, release and asset downloads).
  # A non-empty config token takes precedence over the GITHUB_TOKEN environment variable.
  # Leave empty to use GITHUB_TOKEN, or unauthenticated requests if neither is set.
  github-token: ""

  # IP addresses or CIDRs of proxies allowed to supply forwarded client IP headers.
  # An empty list (the default) does not trust forwarded headers. Restart after changing this setting.
  trusted-proxies: []
  # Example:
  # trusted-proxies:
  #   - 127.0.0.1
  #   - 192.0.2.0/24

  # TLS settings for HTTPS. When enabled, the server listens with the provided certificate and key.
  tls:
    enable: false
    cert: ""
    key: ""

  # When true, disable high-overhead request logging and HTTP middleware features to reduce per-request memory usage under high concurrency.
  commercial-mode: false

  # Enable local network mDNS / DNS-SD service discovery (RFC 6762 / RFC 6763).
  # When enabled, broadcasts _ai-gateway._tcp with API protocol subtypes on the local network.
  # TXT records follow draft-su-ai-gateway-discovery-00:
  #   txtvers, path, tls, auth, protocols, auth_type, product, id, role
  # The TXT protocols list mirrors the advertised subtypes. Management endpoints are never advertised.
  # NOTE for Docker users: Link-local multicast (224.0.0.251) cannot cross default NAT bridge networks.
  # To broadcast across the physical LAN, run the container with host networking (--net=host / network_mode: host).
  discovery:
    enabled: false
    # service-name: ""                  # Optional name prefix. Advertised as CPA-<ShortID> or <name>-<ShortID> (e.g. office-8F3B)
    # service-type: "_ai-gateway._tcp"  # Service type (RFC 6335 compliant)
    # subtypes:                         # DNS-SD API protocol subtypes to advertise
    #   - "_chat-completions"
    #   - "_responses"
    #   - "_messages"
    #   - "_generate-content"
    #   - "_interactions"
    #   # Optional extended subtypes: _completions, _responses-ws, _realtime, _images,
    #   # _videos, _audio, _count-tokens, _search
    # interfaces:
    #   include: []                     # White-listed interfaces (empty: auto-detect physical interfaces)
    #   exclude: []                     # Additional interface patterns to exclude
    # auth-required: true               # Advertise auth=1 (credentials required)

  # Local persistent KV store settings (bbolt). Ignored when Home mode is enabled.
  kv-store:
    # Disable local bbolt KV persistence and fall back to in-memory maps (default: false).
    disabled: false
    # File path for the bbolt database (default: "<auth-dir>/kv.db" when empty).
    path: ""

# Management API settings
management:
  # Whether to allow remote (non-localhost) management access.
  # When false, only localhost can access management endpoints (a key is still required).
  allow-remote: false

  # Management key. If a plaintext value is provided here, it will be hashed on startup.
  # All management requests (even from localhost) require this key.
  # Leave empty to disable the Management API entirely (404 for /v0/management and /v8/management routes).
  secret-key: ""

  # Disable the bundled management control panel asset download and HTTP route when true.
  disable-control-panel: false

  # Disable automatic periodic background updates of the management panel from GitHub (default: false).
  # When enabled, the panel is only downloaded on first access if missing, and never auto-updated afterward.
  # disable-auto-update-panel: false

  # Base URL of remote management API when using TUI client mode (cliproxyapi --tui).
  # Defaults to http://127.0.0.1:<port> if not set. Can also be set via --management-base-url.
  # base-url: "https://proxy.example.com"

  # GitHub repository for the management control panel. Accepts a repository URL or releases API URL.
  panel-github-repository: "https://github.com/router-for-me/Cli-Proxy-API-Management-Center"

# Client authentication for the proxy API. These are not upstream provider keys.
access:
  # API keys for authentication
  api-keys:
    - "your-api-key-1"
    - "your-api-key-2"
    - "your-api-key-3"

# Optional credential concurrency and observation contracts.

# credentials:
#
#   # Credential concurrency is configured by Home in Home mode. The synthesized Home config is
#   # authoritative and local values, including the values below, are ignored. Do not use local
#   # configuration to override a Home concurrency policy.
#
#   concurrency:
#     # Revisions are system-owned metadata, not ordinary editable settings.
#     lifecycle-config-revision: 1
#     observation-barrier-revision: 0
#     cpa-heartbeat-timeout: "3s"
#     cpa-cancel-bound: "5s"
#     reclaim-grace: "5s"
#     cleanup-interval: "5s"
#     release-flush-interval: 250ms
#     release-max-backoff: 2s
#     busy-retry-min: 250ms
#     busy-retry-max: 1s
#     max-limit: 1000000
#
#   # Credential in-flight observation snapshot contract.
#
#   in-flight:
#     snapshot-interval: 2s
#     stale-after: 10s
#     max-part-bytes: 262144
#     max-part-count: 64
#     max-revision-bytes: 16777216
#     max-aggregate-groups: 100000
#     max-details: 10000
#     max-string-bytes: 256
#     staging-retention: 1m

# Routing strategy for selecting credentials when multiple match.
routing:
  strategy: "round-robin" # round-robin (default), weighted-round-robin, fill-first
  # weighted-round-robin uses each credential's integer weight (default 1, maximum 1,000,000).
  # Non-positive weights exclude the credential while this strategy is active.
  # Sticky requests (including inherited and LCP bindings) also consume weighted credits
  # without moving existing bindings. Compensation is scoped to each eligible credential set;
  # priority tiers, transport preferences, and retry/cooldown subsets do not share credits.
  # Pinned requests use the eligible pool before pinning, within the pinned credential
  # priority tier and transport class, even when session-affinity is disabled.
  # Returning to a pool resumes its retained credits.
  # Settled credits are capped at +/-32 times the eligible pool total; ratios are approximate.
  # In-flight offsets are preserved until the pool settles, regardless of commit order.
  # Sticky requests keep their bindings but stop adding compensation when the full
  # credit delta cannot fit the cap; new sessions keep earning credit to avoid starvation.
  # Credits are process-local and reset on restart or selector replacement.
  # In Home mode, compatible Home versions charge retained WS turns from usage after completion;
  # CPA preserves the retained credential and concurrency slot instead of redispatching each turn.
  # For OAuth/file credentials, add a top-level numeric "weight" field to the auth JSON.
  # Enable universal session-sticky routing for all clients.
  # Explicit Claude Code, Codex, OpenCode, pi, and generic (X-Session-ID) session headers
  # are preferred, followed by prompt_cache_key, Responses conversation IDs, legacy body IDs,
  # execution session identity, Merkle LCP prefix/compaction matching, derived session
  # identity, and the existing first-message hash fallback.
  # LCP compaction matching requires at least 2 shared trailing turns; clients whose
  # compaction replaces all earlier turns with a summary should send X-Session-ID (or
  # another explicit session ID), or affinity starts a new binding at every compaction.
  # Automatic failover is always enabled when bound auth becomes unavailable.
  # An established binding outranks credential priority: once a session is bound, that
  # credential is kept even if a higher-priority credential recovers. Credential priority
  # still decides cold bindings, requests without a session, and post-failover rebinding.
  session-affinity: false # default: false
  # How long session-to-auth bindings are retained. Default: 1h
  session-affinity-ttl: "1h"
  # When true (default), subagents (child sessions with parent references) inherit and bind
  # to the parent's upstream credential across all providers (Claude, Codex, Antigravity, Gemini),
  # for the same model, maximizing Prompt/KV Cache reuse and minimizing TTFT.
  # Guardian reviews (X-Openai-Subagent: guardian) may inherit across models from the
  # parent's most recent selection on the same real provider, if that credential is eligible.
  # This includes mixed-provider pools; result-driven TTL refreshes do not reorder selections.
  # Existing child bindings take precedence. Parent lookups do not extend the parent's TTL.
  # When false, subagents are distributed across the credential pool via the fallback selector
  # for maximum parallel concurrency.
  # Ignored when routing.session-affinity is false.
  session-affinity-subagents: true
  # Controls which credential failures rebind an established session to the failover credential:
  #   - "any" (default): any credential-attributed failure unbinds and rebinds on failover.
  #   - "quota": transient errors (408, 5xx, empty stream, buffer limit) fail over for that
  #     request only while keeping the session bound to its original credential; quota and auth
  #     errors (429, 401/402/403, 404/model support, invalid_grant) rebind as normal.
  #   - "never": never move or fail over an established session to another credential;
  #     keep the binding and return the error (or retry the same credential) instead.
  # Ignored when routing.session-affinity is false.
  session-affinity-rebind-on: "any"

  # When true, unprefixed model requests only use credentials without a prefix (except when prefix == model name).
  force-model-prefix: false

  retry:
    # Number of additional credential retry rounds after the first round exhausts
    # its eligible credentials. Round 0 is the initial round; round r only admits
    # credentials whose effective request-retry is at least r. Explicit non-negative
    # credential/provider overrides take precedence; omitted or negative overrides
    # inherit this global value, and explicit 0 only admits round 0. New CPA nodes
    # send retry_round=0 for the initial round and increment it for additional rounds;
    # legacy dispatch methods omit the field and keep old semantics.
    # Additional rounds apply to HTTP 403, 408, 429, 500, 502, 503, and 504 failures.
    # Individual credential/provider overrides take precedence; 0 disables additional
    # rounds, while an omitted or negative override inherits this global setting.
    request-retry: 3

    # Maximum number of different credentials to try in each credential retry round
    # after per-credential round filtering. Set to 0 to try all available
    # credentials. Credentials skipped by this cap still age with the global round,
    # so the cap does not guarantee a fixed number of actual retries per credential.
    max-retry-credentials: 0

    # Maximum cooldown wait in seconds between retry rounds.
    # Set to 0 or below to never wait for credential cooldown.
    # Retry rounds that need no wait remain controlled by request-retry.
    max-retry-interval: 30

  cooldown:
    # When true, disable auth/model cooldown scheduling globally (prevents blackout windows after failure states).
    # A credential/provider disable-cooling value, when present, overrides this global value.
    disable-cooling: false

    # When true, persist per-auth cooldown status as .cds files next to auth files.
    # Default is false; when false, cooldown status is kept in memory only.
    save-cooldown-status: false

    # Cooldown duration in seconds for transient upstream errors (408/500/502/503/504/520-526).
    # Set to 0 to keep the legacy 60-second cooldown; set to -1 to disable transient error cooldowns.
    transient-error-cooldown-seconds: 0

# Shared request/response behavior.
requests:
  # Proxy URL. Supports socks5/http/https protocols. Example: socks5://user:pass@192.168.1.1:1080/
  # Per-entry proxy-url also supports "direct" or "none" to bypass both the global proxy-url and environment proxies explicitly.
  proxy-url: ""
  # Connection pooling has no global switch: it is enabled per api-key credential with
  # `connection-pool` on an api-keys entry (group level, overridable per key). See the
  # api-keys section below. OAuth credentials are never pooled by this setting;
  # Antigravity keeps its own antigravity.connection-pool switch.

  # Maximum time, in seconds, for upstream TLS handshakes made by the shared built-in
  # uTLS client. It applies to both the Claude Code Anthropic transport and the Chrome
  # ChatGPT/Codex transport; the standard fallback transport is unaffected. The timer
  # starts after TCP or proxy connection establishment returns; it does not bound dialing
  # or proxy CONNECT. When the upstream path accepts the connection but never answers the
  # TLS ClientHello, the request would otherwise wait indefinitely and request-retry could
  # never engage. The bound applies only during the TLS handshake and is lifted once it
  # succeeds, so inference and long-running streams are never time-limited.
  # Set to 0 to disable (default).
  tls-handshake-timeout-seconds: 0

  # When true, forward filtered upstream response headers to downstream clients.
  # Default is false (disabled).
  passthrough-headers: false

  # When > 0, emit blank lines every N seconds for non-streaming responses to prevent idle timeouts.
  nonstream-keepalive-interval: 0

  # Streaming behavior (SSE keep-alives + safe bootstrap retries).
  # streaming:
  #   keepalive-seconds: 15         # Default: 0 (disabled). <= 0 disables keep-alives.
  #   bootstrap-retries: 1          # Default: 0 (disabled). Retries before first byte is sent.
  #   send-keepalive-before-ft: false # Default: false. When true and keepalive-seconds > 0, send SSE heartbeats before the first token.

  # Optional payload configuration
  # payload:
  #   default: # Default rules only set parameters when they are missing in the payload.
  #     - models:
  #         - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*")
  #           protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity, meta
  #           from-protocol: "responses" # restricts the rule to the source protocol, options: openai, responses, gemini, claude
  #           headers: # all configured request headers must match; values support "*" wildcards
  #             X-Client-Tier: "tenant-*-region-*"
  #           match: # all payload JSON paths must equal the configured values
  #             - "metadata.client": "codex"
  #           not-match: # payload JSON paths must not equal the configured values
  #             - "metadata.mode": "dev"
  #           exist: # all payload JSON paths must exist and not be null
  #             - "tools.#(type==\"web_search\").type"
  #           not-exist: # all payload JSON paths must be missing or null
  #             - "metadata.disable_payload"
  #       params: # JSON path (gjson/sjson syntax) -> value
  #         "generationConfig.thinkingConfig.thinkingBudget": 32768
  #     - models:
  #         - name: "muse-*"
  #           protocol: "meta"
  #       params:
  #         prompt_cache_retention: "24h" # opt into Meta's 24h prompt cache when the client omits prompt_cache_retention
  #   default-raw: # Default raw rules set parameters using raw JSON when missing (must be valid JSON).
  #     - models:
  #         - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*")
  #           protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity, meta
  #       params: # JSON path (gjson/sjson syntax) -> raw JSON value (strings are used as-is, must be valid JSON)
  #         "generationConfig.responseJsonSchema": "{\"type\":\"object\",\"properties\":{\"answer\":{\"type\":\"string\"}}}"
  #   override: # Override rules always set parameters, overwriting any existing values.
  #     - models:
  #         - name: "gpt-5.4-fast"
  #           protocol: "codex"
  #         - name: "gpt-5.5-fast"
  #           protocol: "codex"
  #       params:
  #         service_tier: priority
  #     - models:
  #         - name: "gpt-*" # Supports wildcards (e.g., "gpt-*")
  #           protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity, meta
  #       params: # JSON path (gjson/sjson syntax) -> value
  #         "reasoning.effort": "high"
  #     - models:
  #         - name: "claude-fable-5-1"
  #           protocol: "claude"
  #       params:
  #         max_tokens: 128000 # override the final Claude Messages output budget after protocol translation
  #   override-raw: # Override raw rules always set parameters using raw JSON (must be valid JSON).
  #     - models:
  #         - name: "gpt-*" # Supports wildcards (e.g., "gpt-*")
  #           protocol: "codex" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity, meta
  #       params: # JSON path (gjson/sjson syntax) -> raw JSON value (strings are used as-is, must be valid JSON)
  #         "response_format": "{\"type\":\"json_schema\",\"json_schema\":{\"name\":\"answer\",\"schema\":{\"type\":\"object\"}}}"
  #   filter: # Filter rules remove specified parameters from the payload.
  #     - models:
  #         - name: "gemini-2.5-pro" # Supports wildcards (e.g., "gemini-*")
  #           protocol: "gemini" # restricts the rule to a specific protocol, options: openai, gemini, claude, codex, antigravity, meta
  #       params: # JSON paths (gjson/sjson syntax) to remove from the payload
  #         - "generationConfig.thinkingConfig.thinkingBudget"
  #         - "generationConfig.responseJsonSchema"
  #     - models:
  #         - name: "gpt-*" # Supports wildcards (e.g., "gpt-*")
  #           protocol: "codex"
  #       params:
  #         # Flat tools array (standard OpenAI format or Codex CLI < 0.154):
  #         - 'tools.#(name=="apply_patch")'
  #         # Nested additional_tools structure in input[] (Codex CLI >= 0.154 "Responses Lite"):
  #         - 'input.#(type=="additional_tools")#.tools.#(name=="functions")#.tools.#(name=="apply_patch")#'

# Client-facing compatibility settings apply to both OAuth and API-key routes.
client:
  codex:
    # When false (default), only models that natively declare freeform apply_patch advertise it.
    # When true, non-template and bridged models supported by every routing executor also advertise "freeform".
    enable-apply-patch: false
    # When true, optimize Codex Desktop, codex-tui, and codex_cli_rs requests for multi-agent v2.
    # This refreshes Codex spawn_agent model details, removes message parameter encryption,
    # normalizes encrypted agent_message content for Codex, and converts agent_message input
    # into standard user messages for non-Codex upstream protocols.
    # Default false. Applies to both OAuth and API-key credentials across providers.
    # Legacy provider paths migrate here; this path wins conflicts, including explicit false.
    optimize-multi-agent-v2: false
    # Cache upstream web search results per session so replayed Codex web_search_call items
    # that omit results still retain search results in later turns.
    web-search-result-cache:
      enabled: false
      ttl: "24h"
      limit: 1024

  claude:
    # Show public Codex commentary as visible Claude text, retaining phase markers
    # for replay. Genuine reasoning summaries remain thinking blocks.
    # Default false preserves existing behavior for clients such as TTS consumers.
    show-codex-commentary: false

# Shared upstream behavior applies to OAuth and API-key credentials.
upstream:
  codex:
    # Experimental full-duplex Codex Responses WebSocket steering (default: false).
    # Keeps one upstream account/model per socket; accepted input is never replayed.
    response-steering: false
    # Compress request bodies sent to the ChatGPT Codex backend with zstd,
    # mirroring the official Codex client. Applies only to OAuth credentials
    # targeting https://chatgpt.com/backend-api/codex (API-key credentials,
    # custom base URLs, and SIWC requests are never compressed). A 400/415
    # rejection is retried once with an uncompressed body. Default: false.
    upstream-request-compression: false
    # Request bodies smaller than this many bytes are sent uncompressed.
    upstream-request-compression-min-bytes: 1024
    # Enable permessage-deflate compression on Codex WebSocket connections
    # (upstream and downstream) after negotiation. Default: true.
    websocket-compression: true
    # Disable forcing the official Codex User-Agent and Originator headers on HTTP/SSE and WebSocket requests.
    disable-codex-cloaking: false
    # Hold back the frames that carry nothing the client has seen - the handshake (response.created,
    # response.in_progress, the websocket metadata frames), keepalive heartbeats, and the *.added
    # announcements of an item or part that is still empty - until the upstream emits its first
    # generated event. Anything else, including a server-side tool item such as web_search_call and
    # any frame not on that list, releases the stream immediately. On the SSE transport the lines that
    # are not `data:` frames - comments, `event:` lines, blank separators - are held alongside them.
    # The hold is bounded by a frame budget and a 1 MiB byte budget, not by wall-clock time: an
    # upstream that trickles frames holds the response headers for as long as the client will wait.
    # Why: the upstream smuggles `server_is_overloaded` rejections *inside* an HTTP 200 stream,
    # right after those handshake events, instead of returning 503 on the wire. Buffering them
    # keeps the downstream response headers uncommitted long enough to transparently retry on
    # another credential. Only overload/rate-limit rejections trigger failover; every other terminal
    # failure is still delivered in-stream exactly as before. One pre-existing behaviour is worth
    # knowing, because this option makes the window it lives in much longer: if the stream ends while
    # the bootstrap is still holding, the attempt ends instead of the error reaching the client, and
    # whether another credential is then tried depends on how it ended. A clean end with no terminal
    # event is request-scoped on SSE, so nothing else is tried; a websocket close, or a transport error
    # such as a dropped connection on either transport, is not, so the request may be retried on
    # another credential. None of that is new, but before this option the hold ended at the first
    # keepalive, so it was rare; now it can span the whole reasoning phase.
    # Trade-off: response headers are delayed until generation starts. Because heartbeats are held
    # too, the delay now ends at the first generated token rather than at the first heartbeat. The
    # budget bounds how much is held, not how long: 48 SSE lines, which measures out as 15 heartbeats
    # of the three-line event:/data:/blank shape, 23 of a two-line ": keepalive" comment followed by
    # its blank separator, or 47 of a bare ": keepalive" with no separator - one short of the obvious
    # division, because the rejection frame's own event: line comes out of the same budget before its
    # data: line is parsed. An upstream that sends the rejection as a bare data: line with no event:
    # line gets the round number instead. At a 10s upstream heartbeat that is 150s, 230s or 470s of
    # uncommitted response headers depending on the framing the upstream picks, all of it past nginx's
    # 60s default proxy_read_timeout, so raise the timeouts in front of the proxy before enabling this.
    # Those seconds bound what the buffer holds, not when the response headers are committed. Headers
    # are committed on the first chunk the downstream translator actually produces, so for a format
    # that renders the handshake as nothing - Chat Completions and Gemini do - they stay uncommitted
    # past the budget, until the first generated token. That is not specific to the budget: any frame
    # that releases the buffer has always behaved this way.
    # The websocket transport spends the same 48 differently - one per message read, whether the loop
    # holds it or skips it, with no framing to divide by - so there it is 48 heartbeats, about 480s.
    # Two cases no budget covers: a websocket peer that sends only ping frames, which the library
    # answers inside the read call, leaving only the 5 minute websocket read deadline; and an upstream
    # that stops mid-line on SSE, where the scanner never returns and only the request context ends the
    # wait. Neither is made worse by this option - with it off the same stalled line stalls the
    # conductor instead, which waits on the same context - but a client-side request timeout, not
    # proxy_read_timeout, is what bounds them.
    # Default: false
    stream-bootstrap-buffering: false
    # Optional maximum duration to hold back uncommitted response headers during bootstrap buffering.
    # When set (e.g. "20s"), if the time ceiling is reached before the first generated token,
    # the stream is released to the client and further in-stream overloads are delivered rather
    # than failed over. This bounds how long trickled heartbeats or early empty frames can delay
    # response headers before streaming begins.
    # Recommendation: If your CPA is deployed behind a reverse proxy with a strict read timeout
    # (e.g. Nginx default proxy_read_timeout 60s or Cloudflare 100s timeout), setting this to "20s"
    # is strongly recommended to guarantee headers commit well before proxy timeouts while retaining
    # a comfortable 20s window to fail over early scheduling overloads.
    # Note: The timeout check is evaluated as upstream frames arrive; it never aborts the upstream
    # connection. Downstream headers are committed as soon as the downstream translator emits chunks
    # (for formats that render handshake/heartbeats, headers commit immediately on release; for
    # formats like Chat Completions and Gemini that render nothing until the first text token, headers
    # commit when that first token arrives).
    # When unset or set to "0", "0s", "none", "unlimited", "disabled", "off", or "never", no time ceiling
    # is applied, and buffering relies purely on the 48-frame and 1MB byte budget.
    # Default: "0" (unlimited)
    stream-bootstrap-timeout: "0"
    # When true, enable opt-in compatibility for orphan Codex delegation outputs.
    # Converts orphan function_call_output items from codex_app/create_thread and
    # codex_app/send_message_to_thread (which lack a valid call_id or matching function_call)
    # into standard user messages across Responses and non-Codex protocols when the request
    # header contains X-Openai-Subagent: collab_spawn.
    orphan-delegation-compatibility: false
    # When true, scope Codex usage_limit_reached quota cooldowns to the requested model rather
    # than cooling down the entire credential across all sibling models.
    # Default: false
    model-level-cooling: false

  claude:
    # When true, scope quota cooldowns to the requested model rather than cooling down
    # the entire credential across all sibling models. Default is false.
    model-level-cooling: false

    # When true, enable Codex client tool_search on Claude Responses routes. Credentials on
    # api.anthropic.com (or keys with native-tool-search: true) receive it as Anthropic native
    # tool search (defer_loading + tool_reference), which keeps the prompt-cache prefix stable
    # across tool discoveries; other credentials fall back to the plain translation. The Codex
    # model catalog advertises supports_search_tool for a Claude model only when every
    # credential serving it supports native tool search.
    # Default false keeps the translation and catalog behavior that predate tool_search support.
    tool-search: false

    # When true, inject Anthropic server-side refusal fallbacks on cloaked Claude requests
    # when the request body does not already specify fallbacks (claude-opus-5-5 -> claude-opus-4-8,
    # claude-fable-5-1 -> claude-opus-5). Default false never opts requests into cross-model
    # fallback unless the caller or requests.payload rules explicitly provide fallbacks.
    # When fallback occurs, translated OpenAI Chat Completions and Responses payloads report
    # the actual serving model and attach structured fallback metadata (choices[0].delta.fallback /
    # choices[0].message.fallback and fallbacks for Chat; response.fallback and response.fallbacks
    # for Responses).
    server-side-fallback: false

    # Days to retain the cloaked currentDate pin per session in in-memory mode
    # (server.kv-store.disabled: true without Home), so an idle gap or a credential
    # switch cannot re-anchor the date and invalidate earlier thinking signatures and the
    # prompt cache. Default 7; an explicit 0 keeps the continuity-scoped pin (1h lifetime).
    # When a KV store (local bbolt or Home KV) is active, session date pins are persisted
    # permanently and session-date-pin-days is ignored.
    session-date-pin-days: 7

    # When true, disable Claude request cloaking on OAuth and API-key CLI profiles (the Claude Code CLI disguise and
    # system prompt replacement), so the original system prompt is passed through to Claude as-is.
    # Credential attributes and explicit per-key cloak modes override this shared default.
    # Default false keeps the per-client "auto" behavior (cloak only non-Claude-Code clients).
    # API-key overrides use api-keys.claude[].keys[].cloak.mode.
    disable-claude-cloak-mode: false

    # When true, return original model IDs in Anthropic model list responses instead of cloaked IDs.
    disable-cloaking-model-list: false

    # Anthropic-Beta is assembled per request rather than sent as a fixed list, matching
    # Claude Code: context-1m sits right after claude-code, mid-conversation-system
    # is added only for models that accept a role=system turn, advanced-tool-use only when
    # the request uses tool search (deferred tools) or another advanced tool-use feature,
    # afk-mode sits between fast-mode and extended-cache-ttl, and server-side-fallback / fallback-credit /
    # structured-outputs trail effort. On direct api.anthropic.com a caller may only ask for
    # betas real Claude Code also sends, and they are placed at their observed positions;
    # anything else is dropped so the outgoing set stays one a real client could produce.
    # Other Anthropic-compatible upstreams still forward caller betas verbatim.
    #
    # Shared Claude CLI fingerprint defaults. Update only after measuring a new Claude Code release.
    # Unconfirmed clients use this CLI baseline. Verified native Claude Code CLI, sdk-cli,
    # sdk-ts, and VSCode requests preserve their measured entrypoint when the Claude Code version is a
    # patch release in the configured major/minor line at or above this baseline. Older versions
    # and newer major/minor lines fall back to the baseline until measured. In legacy mode,
    # timeout is a fallback and
    # verified native OS/arch values remain client-supplied. When stabilize-device-profile is
    # enabled, OS/arch are pinned to the values below and cached profiles remain constrained to
    # the same exact software baseline rather than learning newer client versions (unless the
    # credential resolves to cloak_mode: never, which preserves the confirmed caller's headers).
    # header-defaults:
    #   user-agent: "claude-cli/2.1.321 (external, cli)"
    #   package-version: "0.128.0"
    #   runtime-version: "v26.12.1"
    #   os: "MacOS"
    #   arch: "arm64"
    #   timeout: "600"
    #   timezone: "Asia/Singapore"  # fallback IANA timezone for cloaked currentDate; a credential JSON "timezone" takes priority
    #   stabilize-device-profile: false  # optional, default false; set true to enable per-auth fingerprint pinning
    #   # Additional Claude Code entrypoint labels (beyond built-in cli, sdk-cli, sdk-ts, claude-vscode)
    #   # treated as native Claude Code for passthrough when strong signals match (e.g. T3 Code's "t3-code").
    #   native-entrypoints: ["t3-code"]
    #   # Advanced operator overrides for exact-parity or gateway lanes. They apply to every Claude
    #   # request, after the per-request derivation above, and can produce a beta set real Claude Code
    #   # never sends, which api.anthropic.com may reject. Names are matched exactly; strip runs first.
    #   # A credential's own "headers" entries are more specific and keep precedence where they already
    #   # did: Anthropic-Beta on custom gateways and x-client-request-id everywhere.
    #   anthropic-beta-strip: []   # remove these names from the derived or caller-supplied set, keeping order
    #   anthropic-beta-append: []  # append these names at the end unless already present
    #   # Cloaked requests to api.anthropic.com get a "diagnostics" body field like Claude Code, which in
    #   # turn adds cache-diagnosis-2026-04-07. Set false to stop injecting it (default true).
    #   inject-diagnostics: true
    #   # x-client-request-id on first-party CLI-profile requests: "on" (default) synthesizes a fresh
    #   # UUID per request (a confirmed Claude Code client keeps its own), "passthrough" forwards only
    #   # the caller's value and never synthesizes one, "off" never sends the header.
    #   client-request-id: "on"

  xai:
    # When true, inject the native x_search tool when the request does not declare it.
    # The injected tool is also added to tool_choice.allowed_tools when applicable.
    inject-x-search: false

# API-key upstreams are separate from access.api-keys (client authentication).
# Each provider contains named groups: one base-url, shared settings, and a keys list.
# Add another group for another endpoint; base-url belongs to the group, not a key.
# Shared settings retain their provider-specific validation and meaning.
# Missing/null per-key overrides inherit the group value, then the runtime fallback.
# Explicit false, 0, empty strings, and empty collections override inheritance when
# supported by the field. Map/list overrides replace the inherited value as a whole.
# Negative request-retry values retain the global fallback; weight belongs to each key.
# An empty proxy-url falls back to requests.proxy-url; direct bypasses proxy settings.
# Shared provider defaults belong to upstream; OAuth-only features remain under oauth.providers.
# Keep differing legacy policies in separate groups or preserve them as key overrides.
# Uncomment the api-keys root and only the provider examples you want to configure.
# Replace placeholder keys/URLs before using them.

# BEGIN API KEY EXAMPLES

# api-keys:
#
#   # Gemini API keys
#
#   gemini:
#     - name: gemini-1
#       priority: 0 # larger values take precedence when selecting credentials
#       prefix: "test" # optional: require calls like "test/gemini-3-pro-preview" to target this credential
#       disable-cooling: false # optional override: true disables cooling, false enables it; omit to inherit global
#       request-retry: 3 # optional per-auth override; 0 disables additional rounds; omit or set < 0 to inherit global
#       request-scoped-errors: # optional: custom rules to classify upstream errors by status and body patterns
#         - status: 400        # HTTP status code to match
#           match:             # optional: string contains matching
#             - "maximum_context_length"
#             - "context_length_exceeded"
#           match-regexr:      # optional: regular expression matching
#             - "maximum_context_length$"
#             - "^context_length_exceeded"
#           action: "stop"     # "stop" (return error, no cooling), "stop-and-cooldown" (return error and cool down),
#                              # "continue" (try next credential, no cooling), "continue-and-cooldown" (try next credential and cool down)
#       base-url: "https://generativelanguage.googleapis.com"
#       headers:
#         X-Custom-Header: "custom-value"
#         # Values starting with "$" dynamically copy the header value from downstream client requests.
#         # If the client did not send the specified header, the header is omitted.
#         # X-Claude-Code-Session-Id: "$ABC" # copies client's "ABC" header
#       proxy-url: "socks5://proxy.example.com:1080"
#       # proxy-url: "direct" # optional: explicit direct connect for this credential
#       models:
#         - name: "gemini-2.5-flash" # upstream model name
#           alias: "gemini-flash"    # client alias mapped to the upstream model
#           display-name: "Gemini Flash" # optional catalog display name
#           max-context-length: 1048576 # optional: override Codex client context window metadata
#           is-compat: false             # optional: preserve thinking blocks with empty signatures for compatible upstreams
#           thinking:                    # optional: exact thinking capability for this configured model
#             levels: ["high", "medium", "low", "none", "auto"]
#             # For budget-based models, replace levels with these capabilities:
#             # min: 1024
#             # max: 32768
#             # zero-allowed: true
#             # dynamic-allowed: true
#       excluded-models:
#         - "gemini-2.5-pro"     # exclude specific models from this provider (exact match)
#         - "gemini-2.5-*"       # wildcard matching prefix (e.g. gemini-2.5-flash, gemini-2.5-pro)
#         - "*-preview"          # wildcard matching suffix (e.g. gemini-3-pro-preview)
#         - "*flash*"            # wildcard matching substring (e.g. gemini-2.5-flash-lite)
#       keys:
#         - api-key: "AIzaSy...01"
#           weight: 5 # optional: weighted-round-robin share; omitted defaults to 1; maximum 1,000,000
#         # A second key shares this group's endpoint, headers, and models.
#         - api-key: "replace-with-second-gemini-key"
#           weight: 2
#           priority: null # inherit the group priority
#           prefix: null # inherit the group prefix
#           proxy-url: "direct" # override the group proxy for this key
#           headers: null # inherit the complete group header map
#           models: null # inherit the complete group model list
#           excluded-models: [] # explicitly clear the inherited exclusions
#           disable-cooling: false # an explicit false overrides a true group setting
#           request-retry: 0 # explicitly disable additional rounds for this key
#           request-scoped-errors: null # inherit the group rules
#     - name: gemini-2
#       keys:
#         - api-key: "AIzaSy...02"
#
#   # Native Interactions API keys
#   # These keys are used only for direct /v1beta/interactions execution. Regular api-keys.gemini entries still
#   # send Gemini generateContent/streamGenerateContent requests when the client enters through the interactions API.
#
#   interactions:
#     - name: interactions-1
#       prefix: "native" # optional: require calls like "native/gemini-3-pro-preview" to target this credential
#       disable-cooling: false # optional override: true disables cooling, false enables it; omit to inherit global
#       request-retry: 3 # optional per-auth override; 0 disables additional rounds; omit or set < 0 to inherit global
#       request-scoped-errors: # optional: custom rules to classify upstream errors by status and body patterns
#         - status: 400
#           match:
#             - "invalid_argument"
#           action: "continue"
#       base-url: "https://generativelanguage.googleapis.com"
#       headers:
#         X-Custom-Header: "custom-value"
#         # Values starting with "$" dynamically copy the header value from downstream client requests.
#         # If the client did not send the specified header, the header is omitted.
#         # X-Claude-Code-Session-Id: "$ABC" # copies client's "ABC" header
#       proxy-url: "socks5://proxy.example.com:1080"
#       # proxy-url: "direct" # optional: explicit direct connect for this credential
#       models:
#         - name: "gemini-2.5-flash" # upstream model name
#           alias: "native-gemini-flash" # client alias mapped to the upstream model
#           max-context-length: 1048576 # optional: override Codex client context window metadata
#           is-compat: false             # optional: preserve thinking blocks with empty signatures for compatible upstreams
#           thinking:                    # optional: exact thinking capability for this configured model
#             levels: ["high", "medium", "low", "none", "auto"]
#       excluded-models:
#         - "gemini-2.5-pro"
#       keys:
#         - api-key: "AIzaSy...03"
#           weight: 5 # optional: weighted-round-robin share; omitted defaults to 1; maximum 1,000,000
#
#   # Vertex API keys (Vertex-compatible endpoints, base-url is optional)
#
#   vertex:
#     - name: vertex-1
#       prefix: "test"                              # optional: require calls like "test/vertex-pro" to target this credential
#       disable-cooling: false                       # optional override: true disables cooling, false enables it; omit to inherit global
#       request-retry: 3                             # optional per-auth override; 0 disables additional rounds; omit or set < 0 to inherit global
#       base-url: "https://example.com/api"         # optional, e.g. https://zenmux.ai/api; falls back to Google Vertex when omitted
#       proxy-url: "socks5://proxy.example.com:1080" # optional per-key proxy override
#       # proxy-url: "direct" # optional: explicit direct connect for this credential
#       # interactions: true                        # optional: opt into native Vertex Interactions API routing (/v1beta1/interactions). For service account JSON files under auths/, add "interactions": true at the root of the file.
#       headers:
#         X-Custom-Header: "custom-value"
#         # Values starting with "$" dynamically copy the header value from downstream client requests.
#         # If the client did not send the specified header, the header is omitted.
#         # X-Claude-Code-Session-Id: "$ABC" # copies client's "ABC" header
#       models:                                     # optional: map aliases to upstream model names
#         - name: "gemini-2.5-flash"                # upstream model name
#           alias: "vertex-flash"                   # client-visible alias
#           display-name: "Vertex Flash"            # optional catalog display name
#           thinking:                                # optional: exact thinking capability for this configured model
#             levels: ["high", "medium", "low", "none", "auto"]
#         - name: "gemini-2.5-pro"
#           alias: "vertex-pro"
#       excluded-models:                            # optional: models to exclude from listing
#         - "imagen-3.0-generate-002"
#         - "imagen-*"
#       keys:
#         - api-key: "vk-123..."                        # x-goog-api-key header
#           weight: 5                                    # optional: weighted-round-robin share; omitted defaults to 1; maximum 1,000,000
#
#   # Codex API keys
#
#   codex:
#     - name: codex-1
#       prefix: "test" # optional: require calls like "test/gpt-5-codex" to target this credential
#       disable-cooling: false # optional override: true disables cooling, false enables it; omit to inherit global
#       request-retry: 3 # optional per-auth override; 0 disables additional rounds; omit or set < 0 to inherit global
#       request-scoped-errors: # optional: custom rules to classify upstream errors by status and body patterns
#         - status: 400
#           match:
#             - "context_window_exceeded"
#           action: "stop-and-cooldown"
#       base-url: "https://www.example.com" # use the custom codex API endpoint
#       headers:
#         X-Custom-Header: "custom-value"
#         # Values starting with "$" dynamically copy the header value from downstream client requests.
#         # If the client did not send the specified header, the header is omitted.
#         # X-Claude-Code-Session-Id: "$ABC" # copies client's "ABC" header
#       proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override
#       # proxy-url: "direct" # optional: explicit direct connect for this credential
#       # Optional connection reuse for this credential group. Writing the block is the opt-in;
#       # `connection-pool: {}` opts in with the defaults below. Pools stay per key, so pooling
#       # one key never shares connections with another key. Applies to the standard transport
#       # and the Claude Code TLS transport (Anthropic upstreams); the ChatGPT browser-profile
#       # transport keeps its one-connection-per-request lifecycle.
#       # Defaults: idle-conn-timeout 300s, max-idle-conns-per-host 2. Zero or negative values
#       # disable keep-alives (every request dials). Authenticated proxy URLs are isolated per
#       # proxy credential digest so rotating user:pass or sticky-session usernames never mix.
#       # connection-pool:
#       #   enabled: true
#       #   idle-conn-timeout: "300s" # defaults to 300s; measured upstreams reuse idle conns >600s
#       #   max-idle-conns-per-host: 2 # defaults to 2
#       models:
#         - name: "gpt-5-codex"   # upstream model name
#           alias: "codex-latest" # client alias mapped to the upstream model
#           display-name: "Codex Latest" # optional catalog display name
#           max-context-length: 1048576 # optional: override Codex client context window metadata
#           force-mapping: true    # optional: rewrite response model fields back to the alias
#           # Preserve thinking blocks with empty signatures for compatible upstreams.
#           # Also handle compaction_trigger requests with local summary compaction instead of forwarding them upstream.
#           # client.codex.optimize-multi-agent-v2 applies to both OAuth and API-key credentials.
#           is-compat: false
#           # Optional: enable configuration_update for this API-key model; omitted defaults to false.
#           support-configuration-update: false
#           thinking:              # optional: exact thinking capability for this configured model
#             levels: ["xhigh", "high", "medium", "low"]
#       excluded-models:
#         - "gpt-5.1"         # exclude specific models (exact match)
#         - "gpt-5-*"         # wildcard matching prefix (e.g. gpt-5-medium, gpt-5-codex)
#         - "*-mini"          # wildcard matching suffix (e.g. gpt-5-codex-mini)
#         - "*codex*"         # wildcard matching substring (e.g. gpt-5-codex-low)
#       keys:
#         - api-key: "sk-atSM..."
#           weight: 5 # optional: weighted-round-robin share; omitted defaults to 1; maximum 1,000,000
#           # connection-pool:                # optional: per-key override of the group pool setting
#           #   enabled: false                # opt this key out while the group stays pooled
#           disable-codex-cloaking: false # optional: true disables cloaking; OAuth provider settings do not apply here
#           alpha-search: false # optional: allow this key to serve /v1/alpha/search via base-url + /alpha/search
#           websockets: false # optional: use the Codex upstream WebSocket transport
#
#   # Claude API keys
#
#   claude:
#     - name: claude-1
#       keys:
#         - api-key: "sk-atSM..." # use the official claude API key, no need to set the base url
#     - name: claude-2
#       prefix: "test" # optional: require calls like "test/claude-sonnet-latest" to target this credential
#       disable-cooling: false # optional override: true disables cooling, false enables it; omit to inherit global
#       request-retry: 3 # optional per-auth override; 0 disables additional rounds; omit or set < 0 to inherit global
#       request-scoped-errors: # optional: custom rules to classify upstream errors by status and body patterns
#         - status: 400
#           match:
#             - "prompt is too long"
#           action: "stop"
#       base-url: "https://www.example.com" # use the custom claude API endpoint
#       headers:
#         X-Custom-Header: "custom-value"
#         # Values starting with "$" dynamically copy the header value from downstream client requests.
#         # If the client did not send the specified header, the header is omitted.
#         # X-Claude-Code-Session-Id: "$ABC" # copies client's "ABC" header
#       proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override
#       # proxy-url: "direct" # optional: explicit direct connect for this credential
#       models:
#         - name: "claude-3-5-sonnet-20241022" # upstream model name
#           alias: "claude-sonnet-latest"      # client alias mapped to the upstream model
#           display-name: "Claude Sonnet"      # optional catalog display name
#           max-context-length: 1048576         # optional: override Codex client context window metadata
#           force-mapping: true                 # optional: rewrite response model fields back to the alias
#           is-compat: false                    # optional: preserve thinking blocks with empty signatures for compatible upstreams
#           thinking:                           # optional: exact thinking capability for this configured model
#             levels: ["max", "xhigh", "high", "medium", "low", "minimal", "none", "auto"]
#       excluded-models:
#         - "claude-opus-4-5-20251101" # exclude specific models (exact match)
#         - "claude-3-*"               # wildcard matching prefix (e.g. claude-3-7-sonnet-20250219)
#         - "*-thinking"               # wildcard matching suffix (e.g. claude-opus-4-5-thinking)
#         - "*haiku*"                  # wildcard matching substring (e.g. claude-3-5-haiku-20241022)
#       keys:
#         - api-key: "sk-atSM..."
#           weight: 5 # optional: weighted-round-robin share; omitted defaults to 1; maximum 1,000,000
#           rebuild-mid-system-message: false # optional: default is false; when true, move messages with role "system" into the top-level Claude system field
#           native-tool-search: false # optional: default is false; when true, trust this key's custom base-url with Anthropic native tool search (defer_loading + tool_reference) for Codex tool_search; requires upstream.claude.tool-search: true
#           cloak:                         # optional: explicitly enable request cloaking for non-Claude-Code clients
#             mode: "auto"                 # omitted inherits shared default; explicit "auto": cloak only when client is not Claude Code
#                                          # "always": cloak every unconfirmed client; confirmed native Claude Code still passes through
#                                          # "never" / "uncloak": never apply cloaking; on non-CLI API keys, also strip incoming Claude Code identity/billing attribution
#             # This "cloak" block applies to this api-keys.claude key entry only. For Claude OAuth
#             # credentials, set the same options in the auth/token JSON file via "cloak_mode" /
#             # "cloak_strict_mode" / "cloak_sensitive_words" / "cloak_cache_user_id".
#             # upstream.claude.disable-claude-cloak-mode is the shared default; this key can override it.
#             strict-mode: false           # false (default): legacy-model whitelist uses a user system-reminder;
#                                          # all other and future models use messages[].role=system
#                                          # true: strip caller prompts and keep only Claude Code billing and identity blocks
#             sensitive-words:             # optional: words to obfuscate with zero-width characters
#               - "API"
#               - "proxy"
#             sensitive-words-replace:     # optional: literal sensitive-word replacements ("target": "replacement")
#               "Claude Code": "Claude"
#             cache-user-id: true          # optional: default is false; set true to reuse cached user_id per API key instead of generating a random one each request
#             # Every custom tool on a cloaked OAuth request automatically uses a caller-stable opaque mcp__<server>__<tool> alias.
#
#           # fingerprint-profile (optional, top-level on this api-keys.claude key entry; not a cloak sub-field):
#           # OAuth and API-key fingerprints are different contracts.
#           #   - Real Claude OAuth stays on the strict Claude Code CLI wire fingerprint.
#           #   - API keys (official Anthropic, custom gateways, Kimi) stay loose and
#           #     caller-owned unless this field is set.
#           #
#           # Default (omit / empty): keep the caller request fingerprint and headers.
#           # Official api.anthropic.com API keys do not add extra CLI betas/identity unless
#           # this field is set. Custom gateways and delegated providers are the same.
#           #
#           # Controls request fingerprint only on /v1/messages (and related Claude executor paths).
#           # Auth scheme stays API key (x-api-key on api.anthropic.com; Bearer on custom base-url).
#           # Does NOT enable OAuth refresh, profile fetch, or OAuth-cancellation semantics.
#           #
#           # Values:
#           #   omit / empty        = caller-owned API-key fingerprint (respects caller)
#           #   "claude-code-cli"   = same Messages fingerprint as Claude Code OAuth CLI,
#           #                         including official Anthropic API keys: OAuth Anthropic-Beta
#           #                         set, CCH signing on api.anthropic.com, stable CLI
#           #                         metadata.user_id / session_id / device identity.
#           #                         API keys seed identity from the key;
#           #                         delegated OAuth providers use stable auth ID instead of
#           #                         rotating access tokens. "oauth-cli" is a legacy alias.
#           #
#           # count_tokens keeps the native model/messages/tools shape for every origin, including
#           # Kimi opt-in. It does not send billing/CCH, currentDate, metadata, or diagnostics.
#           #
#           # CCH: the billing block may carry a per-request cch hash. CPA emits it exactly where
#           # Claude Code does, which is api.anthropic.com (first-party) and Vertex only. An opt-in
#           # on any other gateway (including Kimi) still sends the billing block, but without cch,
#           # so a per-request hash cannot bust that gateway's prompt cache. api.anthropic.com
#           # strips the block itself (0 tokens, no cache impact). Kimi drops the whole block by
#           # default and keeps it, unsigned, after an explicit fingerprint opt-in.
#           # A real Claude OAuth credential always signs, on every upstream: a downstream Claude
#           # Code pointed at CPA cannot produce that value itself.
#           #
#           # Example group under api-keys.claude (official Anthropic or a Messages gateway):
#           #   - name: cli-compatible-gateway
#           #     # base-url: "https://gateway.example"   # omit for api.anthropic.com
#           #     keys:
#           #       - api-key: "your-key"
#           #         fingerprint-profile: "claude-code-cli"
#           #         cloak:
#           #           mode: "always"   # recommended when upstream rejects non-CLI clients
#           #
#           # Delegated Anthropic Messages OAuth files (Kimi, etc.) use "fingerprint_profile"
#           # in the auth JSON. Refresh keeps it. Example:
#           #   {
#           #     "type": "kimi",
#           #     "access_token": "...",
#           #     "refresh_token": "...",
#           #     "fingerprint_profile": "claude-code-cli"
#           #   }
#           #   Legacy "fingerprint-profile" credentials remain supported and are normalized at load time.
#           # fingerprint-profile: "claude-code-cli" # optional api-keys.claude key field; default is empty (caller-owned); uncomment to opt in
#           experimental-cch-signing: false # deprecated compatibility field; CCH is generated automatically
#                                           # for real Claude OAuth on any upstream, and for claude-code-cli profiles
#                                           # only on api.anthropic.com; Vertex keeps provider-native signing
#
#   # xAI API keys
#   # Uses the native xAI executor, including its Responses namespace-tool handling.
#
#   xai:
#     - name: xai-1
#       prefix: "xai" # optional: require calls like "xai/grok-4.5" to target this credential
#       disable-cooling: false # optional override: true disables cooling, false enables it; omit to inherit global
#       request-retry: 3 # optional per-auth override; 0 disables additional rounds; omit or set < 0 to inherit global
#       request-scoped-errors: # optional: custom rules to classify upstream errors by status and body patterns
#         - status: 400
#           match:
#             - "rate_limit_exceeded"
#           action: "continue-and-cooldown"
#       base-url: "https://api.x.ai/v1" # xAI-compatible Responses API endpoint
#       headers:
#         X-Custom-Header: "custom-value"
#         # Values starting with "$" dynamically copy the header value from downstream client requests.
#         # If the client did not send the specified header, the header is omitted.
#         # X-Claude-Code-Session-Id: "$ABC" # copies client's "ABC" header
#       proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override
#       # proxy-url: "direct" # optional: explicit direct connect for this credential
#       models:
#         - name: "grok-4.5"       # upstream model name
#           alias: "grok-latest"   # client alias mapped to the upstream model
#           display-name: "Grok Latest" # optional catalog display name
#           max-context-length: 1048576 # optional: override Codex client context window metadata
#           force-mapping: true     # optional: rewrite response model fields back to the alias
#           is-compat: false        # optional: preserve thinking blocks with empty signatures for compatible upstreams
#           thinking:               # optional: exact thinking capability for this configured model
#             levels: ["xhigh", "high", "medium", "low"]
#       excluded-models:
#         - "grok-4.1"             # exclude specific models (exact match)
#         - "grok-3-*"             # wildcard matching prefix
#       keys:
#         - api-key: "xai-..."
#           weight: 5 # optional: weighted-round-robin share; omitted defaults to 1; maximum 1,000,000
#           websockets: true # optional: use the xAI upstream websocket transport for downstream websocket requests
#
#   # Meta API keys (Muse Code)
#   # Uses the native Meta executor targeting https://api.meta.ai/v1.
#
#   meta:
#     - name: meta-1
#       prefix: "meta" # optional: require calls like "meta/muse-spark-1.3" to target this credential
#       disable-cooling: false # optional override: true disables cooling, false enables it; omit to inherit global
#       request-retry: 3 # optional per-auth override; 0 disables additional rounds; omit or set < 0 to inherit global
#       base-url: "https://api.meta.ai/v1" # optional: defaults to https://api.meta.ai/v1
#       proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override
#       models:
#         - name: "muse-spark-1.3" # upstream model name
#           alias: "muse-latest"   # client alias mapped to the upstream model
#       excluded-models:
#         - "muse-spark-1.1"
#       keys:
#         - api-key: "..."
#           weight: 5 # optional: weighted-round-robin share; omitted defaults to 1; maximum 1,000,000
#
#   # OpenAI compatibility providers
#
#   openai-compatibility:
#     - name: "openrouter" # The name of the provider; it will be used in the user agent and other places.
#       disabled: false # optional: set to true to disable this provider without removing it
#       prefix: "test" # optional: require calls like "test/kimi-k2" to target this provider's credentials
#       base-url: "https://openrouter.ai/api/v1" # The base URL of the provider.
#       support-prompt-cache-key: false # optional: derive prompt_cache_key for requests from all input protocols
#       disable-cooling: false # optional provider override: true disables cooling, false enables it; omit to inherit global
#       request-retry: 3 # optional per-provider override; 0 disables additional rounds; omit or set < 0 to inherit global
#       request-scoped-errors: # optional: custom rules to classify upstream errors by status and body patterns
#         - status: 400
#           match:
#             - "maximum_context_length"
#             - "context_length_exceeded"
#           match-regexr:
#             - "maximum_context_length$"
#             - "^context_length_exceeded"
#           action: "stop" # "stop", "stop-and-cooldown", "continue", "continue-and-cooldown"
#       headers:
#         X-Custom-Header: "custom-value"
#         # Values starting with "$" dynamically copy the header value from downstream client requests.
#         # If the client did not send the specified header, the header is omitted.
#         # X-Claude-Code-Session-Id: "$ABC" # copies client's "ABC" header
#       keys:
#         - api-key: "sk-or-v1-...b780"
#           weight: 5 # optional: weighted-round-robin share; omitted defaults to 1; maximum 1,000,000
#           proxy-url: "socks5://proxy.example.com:1080" # optional: per-key proxy override
#           # proxy-url: "direct" # optional: explicit direct connect for this credential
#           # connection-pool: # optional: per-key override of the group pool block
#           #   enabled: true
#         - api-key: "sk-or-v1-...b781" # without proxy-url
#       models: # The models supported by the provider.
#         - name: "moonshotai/kimi-k2:free" # The actual model name.
#           alias: "kimi-k2"               # The alias used in the API.
#           display-name: "Kimi K2"         # optional catalog display name
#           max-context-length: 1048576    # optional: override Codex client context window metadata
#           image: false                   # optional: set true to allow this model on /v1/images/generations and /v1/images/edits (not chat/responses image input)
#           input-modalities: [text, image] # optional: declare /v1/chat/completions and /v1/responses multimodal input for Codex clients. Use [text] for upstreams that reject multimodal tool result content.
#           output-modalities: [text]       # optional: declare output modalities when known
#           is-compat: false                # optional: preserve Claude thinking blocks for compatible upstreams
#           use-max-completion-tokens: false # optional: default false (uses max_tokens); set true to use max_completion_tokens
#           thinking:                      # optional: omit to default to levels ["low","medium","high"]. Exceeding levels (e.g. "max", "xhigh") are clamped to "high"; explicitly declare levels here if the upstream supports them
#             levels: ["low", "medium", "high"]
#         # You may repeat the same alias to build an internal model pool.
#         # The client still sees only one alias in the model list.
#         # Requests to that alias will round-robin across the upstream names below,
#         # and if the chosen upstream fails before producing output, the request will
#         # continue with the next upstream model in the same alias pool.
#         - name: "deepseek-v3.1"
#           alias: "claude-opus-4.66"
#         - name: "glm-5"
#           alias: "claude-opus-4.66"
#         - name: "kimi-k2.5"
#           alias: "claude-opus-4.66"

# END API KEY EXAMPLES

# OAuth/file-backed credentials. Token material stays in the credential store.
oauth:
  # Authentication directory (supports ~ for home directory)
  auth-dir: "~/.cli-proxy-api"

  # Core auth auto-refresh and manual refresh-all worker pool size (OAuth/file-based auth token refresh).
  # When > 0, overrides the default worker count (16).
  # auth-auto-refresh-workers: 16

  # Global OAuth model name aliases (per channel)
  # These aliases rename model IDs for both model listing and request routing.
  # Supported channels: vertex, aistudio, antigravity, claude, codex, kimi, xai, meta.
  # NOTE: These aliases do not apply to any of the upstream groups under api-keys.
  # NOTE: Because aliases affect the merged /v1 model list and merged request routing, overlapping
  # client-visible names can become ambiguous across providers. For strict backend pinning, use
  # unique aliases/prefixes or avoid overlapping names.
  # You can repeat the same name with different aliases to expose multiple client model names.
  # Optional per-entry fields:
  #   fork: true                  # keep the upstream model and also expose the alias as a separate client-visible model
  #   display-name: "Model Name" # override the human-readable name shown in model catalogs
  #   force-mapping: true         # rewrite upstream response model fields back to the client-visible alias (example below uses antigravity only)
  # Per-auth OAuth aliases can also be stored in an OAuth auth JSON file as "model_aliases".
  # Legacy "model-aliases" credentials remain supported and are normalized at load time.
  # They apply only to that selected auth and take precedence over global aliases for the same client-visible alias.
  # Example auth JSON:
  # {
  #   "type": "codex",
  #   "email": "user@example.com",
  #   "model_aliases": [
  #     {"name": "gpt-5.3-codex-spark", "alias": "gpt-5.5"},
  #     {"name": "gpt-5.3-codex-spark", "alias": "gpt-5.4"}
  #   ]
  # }
  # model-alias:
  #   vertex:
  #     - name: "gemini-2.5-pro"
  #       alias: "g2.5p"
  #   aistudio:
  #     - name: "gemini-2.5-pro"
  #       alias: "g2.5p"
  #   antigravity:
  #     - name: "gemini-pro-agent"                   # upstream Antigravity model id
  #       alias: "gemini-3.1-pro-preview"            # client-visible id (Gemini 3.1 Pro Preview)
  #       display-name: "Antigravity Gemini 3.1 Pro" # optional catalog display name
  #       fork: true
  #       force-mapping: true
  #   claude:
  #     - name: "claude-sonnet-4-5-20250929"
  #       alias: "cs4.5"
  #   codex:
  #     - name: "gpt-5"
  #       alias: "g5"
  #   kimi:
  #     - name: "kimi-k2.5"
  #       alias: "k2.5"
  #   xai:
  #     - name: "grok-4.3"
  #       alias: "grok-latest"
  #   meta:
  #     - name: "muse-spark-1.3"
  #       alias: "muse-latest"
  #   sample-provider:                  # plugin provider keys are supported for OAuth plugins
  #     - name: "sample-model-latest"
  #       alias: "sample-latest"

  # OAuth provider model settings (per channel)
  # Allows overriding model-level capabilities (such as context window) for OAuth credentials.
  # Supported channels: vertex, aistudio, antigravity, claude, codex, kimi, xai, meta.
  # settings:
  #   codex:
  #     - name: "gpt-6-sol"
  #       max-context-length: 524288 # overrides context window advertised to Codex clients
  #     - name: "gpt-6-astra"
  #       max-context-length: 1048576

  # OAuth provider excluded models
  # Applies to OAuth/file-backed credentials of each provider.
  # A credential's own non-empty "excluded_models" list (set in its auth JSON file) takes priority
  # over these rules for that credential and is not merged with them. Credentials without their own
  # list (missing, empty, or null) inherit the current rules here; changes to these rules
  # take effect for them on config reload.
  # excluded-models:
  #   vertex:
  #     - "gemini-3-pro-preview"
  #   aistudio:
  #     - "gemini-3-pro-preview"
  #   antigravity:
  #     - "gemini-3-pro-preview"
  #   claude:
  #     - "claude-3-5-haiku-20241022"
  #   codex:
  #     - "gpt-5-codex-mini"
  #   kimi:
  #     - "kimi-k2-thinking"
  #   xai:
  #     - "grok-3-mini"
  #   meta:
  #     - "muse-spark-1.1"

  # OAuth provider request-scoped error rules (custom error classification for OAuth credentials)
  # request-scoped-errors:
  #   vertex:
  #     - status: 400
  #       match:
  #         - "maximum_context_length"
  #         - "context_length_exceeded"
  #       match-regexr:
  #         - "maximum_context_length$"
  #         - "^context_length_exceeded"
  #       action: "stop" # options: "stop", "stop-and-cooldown", "continue", "continue-and-cooldown"
  #   aistudio:
  #     - status: 400
  #       match:
  #         - "invalid_argument"
  #       action: "stop"
  #   antigravity:
  #     - status: 500
  #       match:
  #         - "internal_server_error"
  #       action: "stop-and-cooldown"
  #   claude:
  #     - status: 400
  #       match:
  #         - "prompt is too long"
  #       action: "stop"
  #   codex:
  #     - status: 400
  #       match:
  #         - "context_window_exceeded"
  #       action: "stop"
  #   kimi:
  #     - status: 400
  #       match:
  #         - "length_limit"
  #       action: "stop"
  #   xai:
  #     - status: 400
  #       match:
  #         - "max_tokens_exceeded"
  #       action: "stop"
  #   meta:
  #     - status: 400
  #       match:
  #         - "context_length_exceeded"
  #       action: "stop"

  # Provider options below apply to OAuth/file-backed credentials.
  # API-key credentials use their own group/key options.
  providers:
    aistudio:
      # When true, enable authentication for the WebSocket API (/v1/ws).
      ws-auth: true

    # Codex provider behavior.
    codex:
      # Terminate and relay Codex Live WebRTC audio and DataChannel traffic in this process.
      # This requires inbound UDP reachability. Keep disabled to preserve direct media behavior.
      live-media-relay:
        enabled: false
        # Maximum concurrent media sessions. Zero uses the default of 32.
        max-sessions: 32
        # Reject downstream SDP candidates that target private, loopback, link-local, or unspecified IPs.
        # Keep false for local or trusted-network Codex Desktop connections.
        disable-private-remote-ips: false
        # Public IPv4 or IPv6 address advertised when CPA is behind 1:1 NAT.
        public-ip: ""
        # Optional UDP allocation range. Both values must be set together and provide at least two ports per session.
        udp-port-min: 0
        udp-port-max: 0
        # Optional STUN/TURN servers. TURN credentials are never returned by the JSON config API.
        # Without a concrete global/per-auth proxy-url, WebRTC uses normal direct ICE/STUN/TURN connectivity.
        # With http, https, socks5, or socks5h proxy-url, the OpenAI-facing leg is forced through
        # authenticated ICE-TCP over that proxy and never falls back to UDP or a direct connection.
        # The Codex Desktop-facing leg remains direct, and configured ICE servers still apply to it.
        # ice-servers:
        #   - urls:
        #       - "stun:stun.example.com:3478"
        #   - urls:
        #       - "turn:turn.example.com:3478?transport=udp"
        #     username: "user"
        #     credential: "secret"

      # Identifier (agent_name_hint and User-Agent prefix) for Sign in with ChatGPT (SIWC) OAuth and API requests.
      signin-with-chatgpt-identifier: "CLIProxyAPI"

      # Default headers for Codex OAuth model requests.
      # These are used only for file-backed/OAuth Codex requests when the client
      # does not send the header. `user-agent` applies to HTTP and websocket requests;
      # `beta-features` only applies to websocket requests. They do not apply to api-keys.codex entries.
      # header-defaults:
      #   user-agent: "codex_cli_rs/0.114.0 (Mac OS 14.2.0; x86_64) vscode/1.111.0"
      #   beta-features: "multi_agent"

    # Antigravity provider behavior.
    antigravity:
      # sensitive-words:              # optional: words to obfuscate with combining grapheme joiners (U+034F) in system instructions
      #   - "API"
      #   - "proxy"
      # sensitive-words-replace:      # optional: literal sensitive-word replacements ("target": "replacement") in system instructions
      #   "You are Codex, an agent based on GPT-5.": "You are a helpful coding assistant."
      # connection-pool:              # optional: upstream HTTP connection pool settings
      #   enabled: false             # whether to enable connection pooling (default: false)
      #   idle-conn-timeout: "30s"    # idle keep-alive timeout (default: 30s, capped at 210s)
      #   max-idle-conns-per-host: 2  # max idle connections per host per credential (default: 2)
      # Use credits as a last resort after free-tier Claude quota is exhausted.
      antigravity-credits: true

      # Signature cache validation for thinking blocks (Antigravity/Claude).
      # When true (default), cached signatures are preferred and validated.
      # When false, client signatures are used directly after normalization (bypass mode for testing).
      # signature-cache-enabled: true

      # Bypass mode signature validation strictness (only applies when signature cache is disabled).
      # When true, validates full Claude protobuf tree (Field 2 -> Field 1 structure).
      # When false (default), only checks R/E prefix + base64 + first byte 0x12.
      # signature-bypass-strict: false

    # Devin provider behavior.
    # devin:
    #   sensitive-words:             # optional: words to remove matching lines in system prompts
    #     - "API"
    #     - "proxy"
    #   sensitive-words-replace:     # optional: literal sensitive-word replacements ("target": "replacement") in system prompts
    #     "Claude Code": "Claude"

# Image and video behavior.
multimedia:
  # disable-image-generation supports: false (default), true, "chat", or "passthrough".
  # - true: disable image_generation everywhere (also returns 404 for /v1/images/generations and /v1/images/edits).
  # - "chat": disable image_generation injection on non-images endpoints, but keep /v1/images/generations and /v1/images/edits enabled.
  # - "passthrough": never inject or strip image_generation on non-images endpoints (forward the client payload unchanged); behaves like "chat" on /v1/images/* endpoints.
  # Native Codex Responses-Lite routes reject forced hosted image_generation with HTTP 400; use the Images API instead.
  # Explicit "passthrough" keeps forwarding these requests without capability validation.
  disable-image-generation: false

  # Base model used by the legacy hosted image_generation tool path when a Codex image request is not proxied directly through the Image API.
  # Must start with "gpt-" (case-insensitive). If unset or invalid, defaults to "gpt-5.4-mini".
  # gpt-image-2-base-model: "gpt-5.4-mini"

  # Grok video routes accept grok-imagine-video, grok-imagine-video-1.5,
  # grok-imagine-video-1.5-preview, and grok-imagine-video-1.5-lite.
  # Supported on /openai/v1/videos and the native xAI /v1/videos/* endpoints.
  # The OpenAI-compatible route accepts resolution 480p, 720p, or 1080p (default: 720p).
  # Built-in xAI video models remain available when a refreshed catalog omits them.

  # How long video IDs returned by /openai/v1/videos and xAI video creation stay bound
  # to the credential that created them. Default: 3h.
  video-result-auth-cache-ttl: "3h"

# Application logs, request logs, usage aggregation, and profiling.
observability:
  logs:
    # Enable debug logging
    debug: false

    # When true, write application logs to rotating files instead of stdout
    logging-to-file: false

    # Maximum total size (MB) of log files under the logs directory. When exceeded, the oldest log
    # files are deleted until within the limit. Set to 0 to disable.
    logs-max-total-size-mb: 0

    # Maximum number of error log files retained when request logging is disabled.
    # When exceeded, the oldest error log files are deleted. Default is 10. Set to 0 to disable cleanup.
    error-logs-max-files: 10

    # Record proxy request/response logs. Management requests are excluded.
    request-log: false

  usage:
    # When false, disable in-memory usage statistics aggregation
    usage-statistics-enabled: false

    # How long (in seconds) usage queue items are retained in memory for the Management API.
    # The local Redis RESP usage output is disabled.
    # Default: 60. Max: 3600.
    redis-usage-queue-retention-seconds: 60

  # Enable pprof HTTP debug server (host:port). Keep it bound to localhost for safety.
  pprof:
    enable: false
    addr: "127.0.0.1:8316"

# Standard dynamic library plugins are trusted in-process code. They are disabled by default.
# Build Go examples with go build -buildmode=c-shared for the target GOOS/GOARCH.
# Other languages can implement the same C ABI and JSON method protocol.
# Plugin executors require a matching auth record with the same provider key.
# If the same provider is configured as OpenAI-compatible, the native executor wins.
# Plugin command-line flags and Management API routes are optional capabilities.
# Existing native flags/routes and higher-priority plugin flags/routes cannot be replaced.
# Plugin list Management API reads Logo and ConfigFields from plugin metadata for management UI display.
# Per-plugin enabled only controls plugins.configs.<pluginID>.enabled and does not implicitly change global plugins.enabled.
plugins:
  enabled: false
  dir: "plugins"
  # Additional plugin store registries. The built-in official registry is always included.
  # store-sources:
  #   - "https://example.com/cliproxy-plugins/registry.json"
  # Optional plugin store auth rules. Secrets are read from environment variables.
  # Tokens are not stored in plugin manifests or node status.
  # store-auth:
  #   - match: "https://plugins.example.com/bearer/"
  #     apply-to: ["registry", "artifact"]
  #     type: "bearer"
  #     token-env: "CLIPROXY_PLUGIN_STORE_TOKEN"
  #     allow-insecure: false
  #   - match: "https://plugins.example.com/basic/"
  #     apply-to: ["registry", "artifact"]
  #     type: "basic"
  #     username-env: "CLIPROXY_PLUGIN_STORE_USER"
  #     password-env: "CLIPROXY_PLUGIN_STORE_PASSWORD"
  #     allow-insecure: false
  #   - match: "https://plugins.example.com/header/"
  #     apply-to: ["registry", "artifact"]
  #     type: "header"
  #     header-name: "X-Plugin-Token"
  #     header-value-env: "CLIPROXY_PLUGIN_STORE_HEADER_TOKEN"
  #     allow-insecure: false
  #   - match: "https://api.github.com/repos/example/"
  #     apply-to: ["registry", "artifact"]
  #     type: "github-token"
  #     token-env: "CLIPROXY_PLUGIN_GITHUB_TOKEN"
  #     allow-insecure: false

  # Home owns this revision; it is not an ordinary editable setting.
  # auth-revision: 0

  configs:
    example:
      enabled: true
      priority: 1
      config1: true
      config2: "string"
      config3: 3
      mode: "safe" # enum example: safe, fast
      config-list: ["a", "b"]
      config-object:
        mode: "safe"

# Legacy compatibility reference (comments only; not part of this v8 template).
# Old files and /v0/management still work without a v8 write. Existing legacy-only
# fields remain in their old locations until a successful v8 configuration write.
# When manually mixing layouts, a present new field wins over its legacy equivalent.
# To deliberately use an old field again, remove its corresponding new field first.
# Client keys moved from the old api-keys list to access.api-keys; the new root
# api-keys is reserved for provider groups. These cannot share one YAML key.
#
# Old spellings shown for reference:
#   host: ""                         # now server.host
#   port: 8317                       # now server.port
#   remote-management:               # now management
#     allow-remote: false
#     secret-key: ""
#   api-keys: ["your-api-key-1"]      # now access.api-keys
#   request-retry: 3                 # now routing.retry.request-retry
#   debug: false                     # now observability.logs.debug
#   proxy-url: ""                    # now requests.proxy-url
#   auth-dir: "~/.cli-proxy-api"     # now oauth.auth-dir
#   codex:                           # now oauth.providers.codex
#     disable-codex-cloaking: false
#   codex-api-key:                   # now api-keys.codex groups with keys
#     - api-key: "your-upstream-key"
#       base-url: "https://codex.example.com"
#
# Compatibility-only quota switches have no new v8 counterpart. They remain
# readable/editable in legacy files and through /v0/management, but are omitted
# from the active v8 template:
#   quota-exceeded:
#     switch-project: true
#     switch-preview-model: true
# antigravity-credits moved to oauth.providers.antigravity.antigravity-credits.