Skip to main content
Local proxy presents provider-compatible HTTP routes on loopback, applies configured local transforms, forwards requests to provider endpoints, and records local usage. By default it is a single-operator developer tool with no inbound authentication. Setting CAVEMAN_AUTH_TOKEN turns it into a shared, token-gated service that may bind beyond loopback. See Deploy the proxy for a team. Start it with:
Default address is 127.0.0.1:8787.

Request path

Provider credentials are preserved from inbound requests. Supported environment fallbacks apply only when an integration does not send a credential. Native SSE, Gemini streamed JSON arrays, and AWS EventStream responses are forwarded as they arrive. HTTP error status, response headers, and body bytes are preserved. Provider error events inside an HTTP 200 stream remain HTTP 200 on the wire, as required after the headers have been sent; the local request record carries provider_stream_error. Error detection stores only that code, not the provider’s error message. Complete billed usage remains distinct from successful generation, and an error-bearing call claims no savings. The protocol regression uses local HTTP servers and the wire envelopes accepted by the Anthropic SDK, OpenAI SDK, Google SDK, and Botocore EventStream parser. It checks first-event delivery before upstream completion, exact bytes, final usage, HTTP errors, and stream errors. AWS Converse frame shapes were also checked with Botocore 1.43.89. This is local protocol evidence; it does not certify every provider endpoint, model, entitlement, or live account.

Routes

Anthropic

OpenAI

Google Gemini

Both v1beta and stable v1 are accepted for these three methods. Equivalent bare Gemini paths are also accepted where profile configuration uses them. See Google’s API versions.

Amazon Bedrock

Optional Mantle compatibility route:
Bedrock’s OpenAI-compatible Chat Completions and Responses APIs on Runtime and Mantle are separate protocols and are not implemented by this Bedrock mount. Paths under /bedrock/openai/v1/... or /bedrock/v1/... return 404 before forwarding. Enabling the Anthropic Mantle route does not enable them. AWS and the OpenAI SDK distinguish their endpoint roots and IAM signing services; see the AWS endpoint comparison and OpenAI Bedrock provider.

Azure OpenAI and Vertex AI

Azure mounts under /azure/... after its base URL is configured. Vertex mounts under /vertex/v1/projects/... and supports public Google and Anthropic publisher route forms implemented by adapter. Both are opt-in because endpoint and identity configuration are installation-specific. Vertex Express also accepts Google’s projectless /vertex/v1/publishers/google/models/{model}:generateContent route and the corresponding :streamGenerateContent and :countTokens methods. Caller API keys use Google’s native header; query keys normalize to that header. See Provider authentication for credential conflicts and the existing OAuth path. Vertex’s Google publisher routes also accept :countTokens. Token-count requests retain their request and response bytes in every proxy mode; counts are not recorded as generated model usage or spend. These routes follow the OpenAI SDK, AWS Runtime reference, and Vertex REST reference.

OpenAI-compatible providers

Named compatibility mounts use:
Each mount declares base_url and an environment-variable name containing its credential. Compatibility means HTTP shape, not guaranteed support for every provider extension. One mount is built in. The proxy registers /compat/opencode-go/ with the upstream https://opencode.ai/zen/go and the credential variable OPENCODE_API_KEY. A compat entry with the name opencode-go in caveman.yaml replaces the built-in mount and can set another endpoint. Pi and local OpenClaw wrappers compare the full request URL their host SDK would send with the URL the running proxy would forward. Scheme, host, port, tenant path, and API version must match. Named mounts use the host’s original provider name; native routes use the listener’s published native endpoint. Unknown or credential-bearing base URLs remain direct with a notice. A same-host path mismatch is not a verified route. The proxy publishes credential-free endpoint identities in its run-state file and returns its instance identity on the local health route. Wrappers require that live identity before trusting the file, so a stale process record plus an unrelated listener cannot open the routing gate. Older proxies without endpoint or identity proof leave these host configurations direct. Existing OpenClaw provider identities, model catalogs, fallback settings, and auth profiles remain owned by OpenClaw. Existing managed OpenClaw configurations stay direct until the gateway can establish the same endpoint proof; fresh managed setup still uses its configured Caveman gateway. Endpoint equality is necessary but does not cover host-derived payload policy. Pi preserves supported public compatibility defaults; native OpenAI Chat cache policy and unresolved endpoint-specific attribution can still require direct transport. OpenClaw 2026.8.2 keeps OpenAI Chat, native Responses and native Anthropic routes direct because its public config overlay cannot preserve all private cache, replay, attribution and stream-validation decisions. Supported custom routes also require compatible transport settings and headers. The wrapper reports when proxy compression is off; keeping a host operational on direct transport is not a claim that its traffic was compressed. See the compatibility audit and Pi extension contract. A mount pointing at a loopback relay (litellm, ollama, llama.cpp) also needs a CAVE_SSRF_ALLOWLIST entry; see Security and privacy.
A named mount can serve two wire protocols from one upstream. The credential header follows the request path. A request to /compat/{name}/v1/messages (Anthropic protocol) carries the key in x-api-key. Every other path carries the key in Authorization: Bearer. A real inbound Bearer token keeps its header on every path. OpenCode Go rejects a Bearer header on its Anthropic path, so this rule is necessary for the anthropic-messages models. The header is the only thing the path decides on its own. If the upstream answers the Anthropic Messages protocol, also declare wire_dialect: anthropic on the mount, or its usage blocks are parsed with OpenAI cache semantics and every cache-warm response is recorded as malformed usage, which drops the request from token accounting and from compression eligibility. See Configuration.

Modes

Unknown mode becomes record. Standard local CLI workflows expose record, compress, and pixel; other modes support controlled evaluation paths.

Streaming

Proxy preserves provider streaming protocols and status behavior. Request transforms finish before upstream dispatch; streaming response stays streaming.

Provider authentication

The standalone proxy forwards the credential selected by the caller. Its environment supplies a fallback when the caller does not send a credential. This matters for a persistent listener: an API key in the proxy’s environment must not replace the key chosen by a different client. Gemini, Vertex, and Azure select their native API-key header before the legacy x-api-key alias. That alias remains supported on other provider routes. Explicit API-key headers take precedence over Authorization when both are present. A native api-key header is considered only for Azure, and a native x-goog-api-key header only for Gemini and Vertex. Neither can supply an unrelated provider’s credential. Gemini URL keys are decoded into x-goog-api-key and removed from the upstream URL. Other query parameters retain their original bytes and order. Different keys in the native header or repeated key/$key parameters return HTTP 400 with code cave_provider_credentials_conflict, without sending either key to Google or adding an environment key. When a caller explicitly supplies an OAuth bearer and a URL key, both caller inputs are retained; no environment key is added. This normalization follows Google’s equivalent API-key system parameters and recommended API-key header. Vertex Express uses the same Google API-key normalization, including conflict rejection and preservation of other query bytes. It does not borrow a key from the Gemini environment. An Express key combined with an explicit OAuth bearer returns HTTP 400; the exact synthetic Bearer no-key-required marker can be displaced by a supplied API key. This matches the Google SDK’s separate API-key and OAuth credential modes. Existing Vertex OAuth, quota-project and traffic-type headers remain intact. See Google’s Express-mode sample and SDK credential selection. Azure preserves an explicitly supplied Bearer scheme for Microsoft Entra tokens and the OpenAI v1 SDK’s Bearer API-key flow. Key bytes are opaque: a key starting with eyJ is not automatically relabelled as a token. The exact synthetic Bearer no-key-required marker remains a request for the configured fallback; it is not sent to Azure. These mappings follow Microsoft’s authentication reference, the OpenAI Azure SDK, and the OpenAI client’s Bearer header. Gemini’s native header follows Google’s SDK.

AWS SDK signing

An AWS SigV4 signature covers the request’s authority, path, headers and body. Changing the base URL or adding a cache marker invalidates that signature. The proxy therefore creates a new signature for the final upstream request; it never puts an incoming AWS4-HMAC-SHA256 header inside a Bearer token. See AWS signing documentation and the AWS SDK’s signing code. For an SDK request signed with IAM credentials, the proxy process needs matching AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. Temporary credentials also need the matching AWS_SESSION_TOKEN. The configured Bedrock region must match the request’s signing region. These variables are read at request time. A configured AWS_BEARER_TOKEN_BEDROCK does not override an incoming IAM request. For example, point the SDK at the Bedrock mount while keeping the same region and IAM identity in both processes:
Missing or conflicting signing configuration returns HTTP 400 with code cave_bedrock_sigv4_configuration before the request reaches AWS. The message names the configuration to fix and includes no credential values. The local proxy checks the caller’s requested identity and region; it does not authenticate the old signature as an IAM security boundary. The proxy currently reads IAM signing credentials from those environment variables. It does not itself load AWS profiles, invoke SSO, assume roles, or refresh a provider credential chain. An SDK using those mechanisms must make its current credentials available to the proxy as well. Alternatively use a Bedrock bearer API key on a supported API-key route.

Verification scope

The authentication regressions exercise the full standalone request pipeline with a capture transport. They cover caller-versus-environment precedence, provider isolation, Azure Bearer and opaque API keys, IAM and temporary AWS credentials, post-transform payload signing, and rejection of unresolved signing identities. Gemini cases cover query/header conflicts and removal of URL credentials. A local HTTP server also checks Bedrock compressed request bytes, encoding headers and signing. These tests do not make paid provider calls or certify every provider endpoint, model, region or account entitlement.

Credentials

API keys stay outside YAML. Anthropic, OpenAI, Gemini, and Azure use their named environment variables; Bedrock uses supported AWS or bearer-token identity paths. Never log authorization headers. Local telemetry should store usage and bounded metadata, not raw secrets.

Endpoint security

Proxy rejects a non-loopback listen address unless CAVEMAN_AUTH_TOKEN is set. With that token every request must present it in x-cave-api-key or Authorization: Bearer; the proxy consumes the header before resolving a provider credential, so it never reaches a provider. Health and metrics endpoints stay unauthenticated. Outbound Server-Side Request Forgery protection checks configured endpoints and redirects. Private, link-local, and loopback upstreams are blocked unless explicitly included in CAVE_SSRF_ALLOWLIST for a self-hosted setup; link-local and metadata addresses have no allowlist escape. See Security and privacy before allowing a local model endpoint.

Pricing and usage

Provider catalog supplies dated public list prices. Unknown provider or model prices resolve to zero with an unpriced marker rather than a guessed cost. Provider-reported token counts remain distinct from Engine estimates. Displayed provider cost is a list-price subtotal, not a provider invoice. See Accounting and evidence.

Troubleshooting

  • A 404 often means the agent uses the wrong provider mount or bare route.
  • Authentication failures should be checked at the inbound header and provider credential source without printing secret values.
  • A blocked custom base URL usually needs a precise CAVE_SSRF_ALLOWLIST entry.
  • Unexpected unchanged context is valid when mode is record or a transform fails parse, size, policy, or recovery gates.
  • For behavior comparison, repeat the request in record mode and compare provider request and response classes, not secret-bearing raw logs.