# How the Caveman Proxy Preserves Credentials While Routing Through Different Provider Adapters

> Learn how the Caveman proxy safeguards credentials by using a Proxy-Authorization header, stripping them before forwarding to ensure secure LLM provider routing.

- Repository: [Julius Brussee/caveman](https://github.com/JuliusBrussee/caveman)
- Tags: how-to-guide
- Published: 2026-09-04

---

**Caveman isolates proxy authentication data from LLM provider requests by attaching credentials only to the proxy hop via a `Proxy-Authorization` header that is explicitly stripped before forwarding to downstream adapters.**

JuliusBrussee/caveman implements a secure credential preservation system that ensures usernames and passwords embedded in proxy URLs never reach the actual LLM providers. The architecture achieves this through a strict separation between proxy connection logic and provider routing logic, implemented across the [`packages/cli/src/proxy-fetch.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/cli/src/proxy-fetch.ts) and [`packages/pi-extension/src/provider.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/pi-extension/src/provider.ts) modules.

## Core Architectural Mechanisms

Caveman employs two cooperating mechanisms to ensure credentials remain confined to the proxy layer.

### Proxy-Only Authorization Headers

When resolving proxy configurations, the `proxyAuthHeader` function in [`packages/cli/src/proxy-fetch.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/cli/src/proxy-fetch.ts) creates a `Proxy-Authorization` header containing the base-64 encoded credentials extracted from the proxy URL. This header is attached **only** to the connection established with the proxy server itself.

The implementation checks for credentials at lines 107-110:

```typescript
// From packages/cli/src/proxy-fetch.ts#L107-L110
function proxyAuthHeader(proxyUrl: URL): Record<string, string> {
  if (!proxyUrl.username) return {};
  const cred = `${decodeURIComponent(proxyUrl.username)}:${decodeURIComponent(proxyUrl.password)}`;
  return { "proxy-authorization": `Basic ${Buffer.from(cred).toString("base64")}` };
}

```

### Explicit Header Sanitization

The `createProxyAwareFetch` wrapper explicitly filters incoming `proxy-authorization` headers to prevent client injection. At lines 22-28 in [`packages/cli/src/proxy-fetch.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/cli/src/proxy-fetch.ts), the code skips copying any caller-supplied proxy authorization into the outgoing request:

```typescript
// Sanitization logic from createProxyAwareFetch
const headers: Record<string, string> = {};
for (const [key, value] of Object.entries(init?.headers || {})) {
  if (key.toLowerCase() === 'proxy-authorization') continue; // Strip client-injected credentials
  headers[key] = value;
}

```

This ensures that clients cannot accidentally or maliciously leak proxy credentials to the target LLM service.

## The Routing Pipeline

When forwarding requests to different provider adapters, Caveman executes a five-step pipeline that keeps credentials isolated:

1. **Resolve proxy** – The `resolveProxyUrl` function inspects `http_proxy`, `https_proxy`, and `all_proxy` environment variables to construct a `URL` object for the proxy connection (`packages/cli/src/proxy-fetch.ts#L95-L104`).

2. **Attach proxy credentials** – The `proxyAuthHeader` function generates the `Proxy-Authorization` header if the proxy URL contains a username and password (`packages/cli/src/proxy-fetch.ts#L107-L110`).

3. **Strip inbound proxy-auth** – The request wrapper removes any existing `proxy-authorization` headers from the incoming request before processing (`packages/cli/src/proxy-fetch.ts#L22-L28`).

4. **Register routed provider** – The `ProviderRouter.apply` method records the original provider base URL in an internal `originals` map, computes the routed endpoint via `routeForApi` from [`packages/pi-extension/src/protocol.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/pi-extension/src/protocol.ts), and registers the provider with the new proxy-routed base URL (`packages/pi-extension/src/provider.ts#L70-L84`).

5. **Send request** – The proxy either opens a CONNECT tunnel for HTTPS traffic via `openTunnel` or forwards the raw HTTP request via `sendThroughProxy`, attaching the proxy authentication header exclusively to the proxy connection (`packages/cli/src/proxy-fetch.ts#L84-L94`, `L125-L135`).

## Key Implementation Files

### packages/cli/src/proxy-fetch.ts

This module implements environment-variable proxy resolution, CONNECT tunneling for HTTPS, and the credential isolation logic. The `installProxyAwareFetch` function wraps the global fetch to intercept requests and inject proxy handling:

```typescript
import { installProxyAwareFetch } from "@caveman/cli";

// Install proxy-aware fetch that respects credentials in http_proxy env vars
installProxyAwareFetch(process.env);

```

### packages/pi-extension/src/provider.ts

The `ProviderRouter` class manages provider overrides and stores original URLs to prevent credential re-exposure. Lines 17-25 define the `originals` map that tracks upstream endpoints:

```typescript
// From packages/pi-extension/src/provider.ts
export class ProviderRouter {
  private originals = new Map<string, string>();
  
  // Stores original provider URLs to maintain routing consistency
  async apply(model: string, ctx: Context) {
    // Registration logic preserves credential isolation
  }
}

```

### packages/pi-extension/src/protocol.ts

This file provides routing helpers like `routeForApi` that compute the correct upstream URL for a given provider without exposing proxy credentials in the resulting endpoint.

## Practical Usage Examples

### Installing the Proxy-Aware Fetch

Enable credential-preserving proxy routing in your Caveman CLI setup:

```typescript
import { installProxyAwareFetch } from "@caveman/cli";

// The wrapper automatically detects http_proxy environment variables
// and ensures credentials stay on the proxy hop only
installProxyAwareFetch(process.env);

```

### Routing Through Provider Adapters

Use `ProviderRouter` to route model requests while maintaining credential isolation:

```typescript
import { ProviderRouter } from "@caveman/pi-extension";

const router = new ProviderRouter(piApi, (msg, kind) => console.warn(msg));
await router.openGate("http://127.0.0.1:8787", ctx, compatUpstreams);

// Routes to the provider while keeping proxy credentials confined
await router.apply(ctx.model, ctx);

```

### Manual Proxy Authentication Construction

The following illustrates the base-64 encoding logic Caveman uses internally for the `Proxy-Authorization` header:

```typescript
function makeProxyAuthHeader(proxyUrl: string): Record<string, string> {
  const url = new URL(proxyUrl);
  if (!url.username) return {};
  const cred = `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}`;
  return { "proxy-authorization": `Basic ${Buffer.from(cred).toString("base64")}` };
}

```

## Summary

- Caveman preserves credentials while routing through different provider adapters by isolating proxy authentication to the connection hop only.
- The `proxyAuthHeader` function in [`packages/cli/src/proxy-fetch.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/cli/src/proxy-fetch.ts) generates `Proxy-Authorization` headers exclusively for the proxy connection.
- Incoming `proxy-authorization` headers are stripped by `createProxyAwareFetch` to prevent credential leakage to LLM providers.
- The `ProviderRouter` class in [`packages/pi-extension/src/provider.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/pi-extension/src/provider.ts) maintains original provider URLs separately from proxy routing logic.
- Credentials embedded in `http_proxy` or `https_proxy` environment variables never reach downstream provider adapters.

## Frequently Asked Questions

### Can clients inject their own proxy credentials into the request?

No. The `createProxyAwareFetch` wrapper in [`packages/cli/src/proxy-fetch.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/cli/src/proxy-fetch.ts) explicitly filters out any `proxy-authorization` headers from incoming requests at lines 22-28. This prevents clients from accidentally or maliciously forwarding proxy credentials to the target LLM service.

### How does Caveman handle HTTPS traffic through the proxy?

For HTTPS destinations, Caveman opens a CONNECT tunnel via the `openTunnel` function in [`packages/cli/src/proxy-fetch.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/cli/src/proxy-fetch.ts) (lines 125-135). The proxy authentication header is attached only during the tunnel establishment phase, ensuring encrypted traffic between the client and final provider remains opaque to the proxy credentials.

### Where are the original provider URLs stored during routing?

Original provider URLs are stored in the private `originals` Map within the `ProviderRouter` class defined in [`packages/pi-extension/src/provider.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/pi-extension/src/provider.ts) (lines 17-25). This map maintains the mapping between model names and their upstream endpoints without exposing proxy authentication details.

### Is the Proxy-Authorization header sent to the LLM provider?

No. The `Proxy-Authorization` header is strictly confined to the proxy connection hop. According to the implementation in [`packages/cli/src/proxy-fetch.ts`](https://github.com/JuliusBrussee/caveman/blob/main/packages/cli/src/proxy-fetch.ts), this header is either generated from the proxy URL credentials or stripped from incoming requests, but never forwarded to the downstream provider adapter during routing.