Skip to main content

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 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"
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.

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"
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. 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.
Provider / APIHostAuth typeNotes
Anthropicapi.anthropic.comapi_keyUse header_name = "x-api-key". bearer will cause 401s.
OpenAIapi.openai.combearerAgents may need a dummy API key so they choose API-key auth instead of OAuth.
GitHub RESTapi.github.combearerCan read the gh token from the keyring with a custom service.
Stripeapi.stripe.combearerStripe 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"
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"