Skip to main content
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 and 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

[[routes]]
name = "stripe"

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

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

[routes.auth.secret]
source = "1password"
account = "my-team"
vault = "Engineering"
item = "Stripe API Key"
field = "secret_key"
FieldTypeRequiredDescription
namestringYesIdentifier for the route (used in logs).
match.hoststringYesHostname to match (case-insensitive).
auth.typestringYesheader or basic.
auth.usernamestringbasic onlyUsername for HTTP Basic authentication.
auth.header_namestringheader onlyHeader name for header auth.
auth.schemestringheader onlyOptional HTTP auth scheme. Required when auth.header_name is Authorization.
auth.secretobjectYesSecret source reference. See Secret Sources.
When auth.secret.source is "1password", the following fields are available:
FieldTypeRequiredDescription
auth.secret.sourcestringYes"1password"
auth.secret.accountstringNo1Password account identifier — sign-in address, shorthand, account UUID, or user UUID. Recommended for multi-account and service setups. See the 1Password guide.
auth.secret.vaultstringYesVault name within the account.
auth.secret.itemstringYesItem name within the vault.
auth.secret.fieldstringYesField label within the item (case-sensitive).

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.
[routes.auth]
type = "header"
header_name = "Authorization"
scheme = "Bearer"

[routes.auth.secret]
source = "1password"
account = "my-team"
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"

Raw API key header

Injects a custom header with the secret value.
[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
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. For non-credentialed access to denylist-blocked destinations, see denylist_exceptions instead. 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.
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.
Route names share one namespace with PostgreSQL listener names and must be unique. See 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. All providers below use type = "header". The table shows the specific header_name and scheme to use for each.
Provider / APIHostheader_nameschemeNotes
Anthropicapi.anthropic.comx-api-key(omit)Do not set scheme. Using Authorization: Bearer will cause 401s.
OpenAIapi.openai.comAuthorizationBearerAgents may need a dummy API key so they choose API-key auth instead of OAuth.
GitHub RESTapi.github.comAuthorizationBearerCan read the gh token from the keyring with a custom service.
Stripeapi.stripe.comAuthorizationBearerStripe 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.

Multiple routes

You can configure multiple routes for different APIs:
[[routes]]
name = "anthropic"

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

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

[routes.auth.secret]
source = "1password"
account = "my-team"
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"
account = "my-team"
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"
account = "my-team"
vault = "Engineering"
item = "Stripe API Key"
field = "secret_key"