> ## Documentation Index
> Fetch the complete documentation index at: https://docs.codezero.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Routes

> Configure which hosts are intercepted and how credentials are injected.

Routes define which outbound requests cordon intercepts and what credentials to inject. Each route matches on a destination hostname and specifies an auth type.

Prefer [`cordon route add`](/cli/route#cordon-route-add) and [`cordon route edit`](/cli/route#cordon-route-edit) over hand-editing routes in `cordon.toml`. The CLI validates hostnames, auth types, headers, and secret-source fields, and is meant as the safer way to produce this configuration. The TOML below is the reference format when you need to inspect, review, or automate outside the CLI.

## Route structure

```toml theme={null}
[[routes]]
name = "stripe"

[routes.match]
host = "api.stripe.com"

[routes.auth]
type = "header"
header_name = "Authorization"
scheme = "Bearer"

[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "Stripe API Key"
field = "secret_key"
```

| Field              | Type   | Required      | Description                                                                     |
| ------------------ | ------ | ------------- | ------------------------------------------------------------------------------- |
| `name`             | string | Yes           | Identifier for the route (used in logs).                                        |
| `match.host`       | string | Yes           | Hostname to match (case-insensitive).                                           |
| `auth.type`        | string | Yes           | `header` or `basic`.                                                            |
| `auth.username`    | string | `basic` only  | Username for HTTP Basic authentication.                                         |
| `auth.header_name` | string | `header` only | Header name for header auth.                                                    |
| `auth.scheme`      | string | `header` only | Optional HTTP auth scheme. Required when `auth.header_name` is `Authorization`. |
| `auth.secret`      | object | Yes           | Secret source reference. See [Secret Sources](/configuration/secret-sources).   |

## Auth types

### Header auth

Injects a configured header with the secret value. Set `scheme` for `Authorization: Bearer <secret>` style headers, or omit it for raw API-key headers.

```toml theme={null}
[routes.auth]
type = "header"
header_name = "Authorization"
scheme = "Bearer"

[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "Stripe API Key"
field = "secret_key"
```

### Basic auth

Injects an `Authorization: Basic <base64(username:secret)>` header.

```toml theme={null}
[routes.auth]
type = "basic"
username = "myuser"

[routes.auth.secret]
source = "keyring"
account = "my-basic-cred"
```

### Raw API key header

Injects a custom header with the secret value.

```toml theme={null}
[routes.auth]
type = "header"
header_name = "X-Api-Key"

[routes.auth.secret]
source = "keyring"
account = "example-api-key"
```

`header_name` must be a valid HTTP field name and cannot be one of Cordon's reserved credential injection headers: `Host`, `Content-Length`, `Transfer-Encoding`, `Connection`, `Keep-Alive`, `TE`, `Trailer`, `Upgrade`, `Proxy-Authorization`, or `Proxy-Authenticate`.

## How matching works

* Cordon matches on the exact **hostname** of the outbound request after canonicalization (case-insensitive, trailing dot ignored). Wildcards and subdomains are not matched automatically.
* For matched routes, cordon strips `Authorization`, `Proxy-Authorization`, and the configured credential header before injection.
* Unmatched requests are forwarded without modification
* Routes are evaluated in order; the first match wins

<Note>
  Configured HTTP routes are explicit trust decisions. In v1, matched routes bypass private/link-local/loopback SSRF denylist checks so internal APIs, VPN/private endpoints, PrivateLink services, staging environments, and localhost development services continue to work. DNS pinning still applies, but DNS pinning is not private-IP blocking. Routes authorize credential injection and upstream selection for the configured host; they do not protect against malicious same-user callers. Post-v1 private-upstream policy work will revisit this default.
</Note>

<Warning>
  Auth stripping is unconditional for matched routes. If your application sends `Authorization: Bearer placeholder`, cordon removes it and injects the real token. This is by design — it prevents accidental credential leakage if the application has a real token.
</Warning>

Route names share one namespace with PostgreSQL listener names and must be unique. See [Listeners](/configuration/listeners) for PostgreSQL listener configuration.

## Provider auth quick reference

Use the auth type expected by the upstream API, even if your app or agent only sends a dummy credential. Cordon strips any inbound credential on matched routes and injects the real value from the configured [secret source](/configuration/secret-sources).

| Provider / API | Host                | Auth type | Notes                                                                         |
| -------------- | ------------------- | --------- | ----------------------------------------------------------------------------- |
| Anthropic      | `api.anthropic.com` | `api_key` | Use `header_name = "x-api-key"`. `bearer` will cause 401s.                    |
| OpenAI         | `api.openai.com`    | `bearer`  | Agents may need a dummy API key so they choose API-key auth instead of OAuth. |
| GitHub REST    | `api.github.com`    | `bearer`  | Can read the `gh` token from the keyring with a custom service.               |
| Stripe         | `api.stripe.com`    | `bearer`  | Stripe API keys are sent as bearer tokens.                                    |

If an application needs a provider-specific env var to select an auth path, set a placeholder such as `dummy-replaced-by-cordon`. The placeholder is not trusted: Cordon removes it and injects the real credential.

## Route changes and secret rotation

Cordon loads route definitions when the proxy starts. Restart Cordon after adding or editing routes. HTTP route secrets are fetched per request, so rotating the secret value in 1Password or the keyring does not require a restart. PostgreSQL listener secrets are different; see [Secret Sources](/configuration/secret-sources#rotation-and-restarts).

## Multiple routes

You can configure multiple routes for different APIs:

```toml theme={null}
[[routes]]
name = "anthropic"

[routes.match]
host = "api.anthropic.com"

[routes.auth]
type = "header"
header_name = "x-api-key"

[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "Anthropic API Key"
field = "credential"

[[routes]]
name = "openai"

[routes.match]
host = "api.openai.com"

[routes.auth]
type = "header"
header_name = "Authorization"
scheme = "Bearer"

[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "OpenAI API Key"
field = "credential"

[[routes]]
name = "stripe"

[routes.match]
host = "api.stripe.com"

[routes.auth]
type = "header"
header_name = "Authorization"
scheme = "Bearer"

[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "Stripe API Key"
field = "secret_key"
```
