Routes define which outbound requests cordon intercepts and what credentials to inject. Each route matches on a destination hostname and specifies an auth type.
Route structure
[[routes]]
name = "stripe"
[routes.match]
host = "api.stripe.com"
[routes.auth]
type = "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 | bearer, basic, or api_key. |
auth.username | string | basic only | Username for HTTP Basic authentication. |
auth.header_name | string | api_key only | Custom header name for API key injection. |
auth.secret | object | Yes | Secret source reference. See Secret Sources. |
Auth types
Bearer token
Injects an Authorization: Bearer <secret> header.
[routes.auth]
type = "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.
[routes.auth]
type = "basic"
username = "myuser"
[routes.auth.secret]
source = "keyring"
account = "my-basic-cred"
Injects a custom header with the secret value.
[routes.auth]
type = "api_key"
header_name = "X-Api-Key"
[routes.auth.secret]
source = "keyring"
account = "example-api-key"
How matching works
- Cordon matches on the hostname of the outbound request (case-insensitive)
- For matched routes, cordon strips any existing auth header and replaces it with the real credential
- Unmatched requests are forwarded without modification
- Routes are evaluated in order; the first match wins
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.
Multiple routes
You can configure multiple routes for different APIs:
[[routes]]
name = "anthropic"
[routes.match]
host = "api.anthropic.com"
[routes.auth]
type = "bearer"
[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 = "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 = "bearer"
[routes.auth.secret]
source = "1password"
vault = "Engineering"
item = "Stripe API Key"
field = "secret_key"
PostgreSQL services
For database connections, cordon can inject credentials at the wire protocol level using TCP services instead of HTTP routes:
[[services]]
name = "prod-db"
listen = 15432
upstream = "db.prod.example.com:5432"
protocol = "postgres"
[services.auth]
type = "password"
username = "app_user"
[services.auth.secret]
source = "1password"
vault = "Engineering"
item = "Postgres Prod"
field = "password"
Connect your application to localhost:15432 instead of the upstream database. Cordon injects the password during the PostgreSQL authentication handshake.