Complete reference for all Chaperone configuration options. Chaperone uses YAML configuration with environment variable overrides following the 12-Factor App methodology.
Chaperone resolves the configuration file path in this order:
CHAPERONE_CONFIGenvironment variable —export CHAPERONE_CONFIG=/etc/chaperone.yaml./config.yaml— current working directory (default)
If no configuration file is found, Chaperone exits with an error.
Default binary only: The pre-built
cmd/chaperonebinary also accepts a-configflag (highest priority). Custom plugin binaries receive configuration viachaperone.Run()options and use the resolution order above.
Environment variables follow the pattern:
CHAPERONE_<SECTION>_<KEY>
Rules:
- All uppercase, underscore separator
- Nested keys use underscore joining:
server.tls.cert_file→CHAPERONE_SERVER_TLS_CERT_FILE - Environment variables always override YAML values
- Duration values use Go syntax:
5s,30s,2m,1h
# Examples
export CHAPERONE_SERVER_ADDR=":9443"
export CHAPERONE_OBSERVABILITY_LOG_LEVEL="debug"
export CHAPERONE_SERVER_TLS_CERT_FILE="/etc/certs/server.crt"
export CHAPERONE_UPSTREAM_TIMEOUTS_CONNECT="10s"Controls the proxy and admin server listeners.
server:
addr: ":443"
admin_addr: "127.0.0.1:9090"
shutdown_timeout: 30s
tls:
enabled: true
cert_file: "/certs/server.crt"
key_file: "/certs/server.key"
ca_file: "/certs/ca.crt"
auto_rotate: true| Key | Env Override | Type | Default | Description |
|---|---|---|---|---|
addr |
CHAPERONE_SERVER_ADDR |
string | :443 |
Traffic port address |
admin_addr |
CHAPERONE_SERVER_ADMIN_ADDR |
string | 127.0.0.1:9090 |
Admin/metrics port (bound to localhost for security) |
shutdown_timeout |
CHAPERONE_SERVER_SHUTDOWN_TIMEOUT |
duration | 30s |
Max time to drain in-flight requests during graceful shutdown |
| Key | Env Override | Type | Default | Description |
|---|---|---|---|---|
tls.enabled |
CHAPERONE_SERVER_TLS_ENABLED |
bool | true |
Enable TLS/mTLS (production builds enforce HTTPS-only targets) |
tls.cert_file |
CHAPERONE_SERVER_TLS_CERT_FILE |
string | /certs/server.crt |
Path to PEM-encoded server certificate |
tls.key_file |
CHAPERONE_SERVER_TLS_KEY_FILE |
string | /certs/server.key |
Path to PEM-encoded server private key |
tls.ca_file |
CHAPERONE_SERVER_TLS_CA_FILE |
string | /certs/ca.crt |
Path to PEM-encoded CA certificate for client verification |
tls.auto_rotate |
CHAPERONE_SERVER_TLS_AUTO_ROTATE |
bool | true |
Enable automatic certificate rotation (Phase 3) |
Controls target request routing, context header parsing, and timeouts.
upstream:
header_prefix: "X-Connect"
trace_header: "Connect-Request-ID"
allow_list:
"api.vendor.com":
- "/v1/**"
- "/v2/**"
"payments.example.com":
- "/api/charge"
- "/api/refund"
timeouts:
connect: 5s
read: 30s
write: 30s
idle: 120s
keep_alive: 30s
plugin: 10s| Key | Env Override | Type | Default | Description |
|---|---|---|---|---|
header_prefix |
CHAPERONE_UPSTREAM_HEADER_PREFIX |
string | X-Connect |
Prefix for context headers (configurable per ADR-005) |
trace_header |
CHAPERONE_UPSTREAM_TRACE_HEADER |
string | Connect-Request-ID |
Correlation ID header name |
allow_list |
— | map | Required | Host or host:port → path patterns (see Allow-List Syntax) |
| Key | Env Override | Type | Default | Description |
|---|---|---|---|---|
timeouts.connect |
CHAPERONE_UPSTREAM_TIMEOUTS_CONNECT |
duration | 5s |
TCP connection establishment timeout to vendor API |
timeouts.read |
CHAPERONE_UPSTREAM_TIMEOUTS_READ |
duration | 30s |
Time to read response headers from vendor |
timeouts.write |
CHAPERONE_UPSTREAM_TIMEOUTS_WRITE |
duration | 30s |
Max time to write the full response back to the platform |
timeouts.idle |
CHAPERONE_UPSTREAM_TIMEOUTS_IDLE |
duration | 120s |
Keep-alive connection idle timeout |
timeouts.keep_alive |
CHAPERONE_UPSTREAM_TIMEOUTS_KEEP_ALIVE |
duration | 30s |
TCP keep-alive probe interval |
timeouts.plugin |
CHAPERONE_UPSTREAM_TIMEOUTS_PLUGIN |
duration | 10s |
Maximum time for plugin GetCredentials call |
Controls logging, profiling, and header redaction.
observability:
log_level: "info"
enable_profiling: false
enable_tracing: false
log_target_addr: "host"
sensitive_headers:
- "X-Custom-Secret"
- "X-Vendor-Token"| Key | Env Override | Type | Default | Description |
|---|---|---|---|---|
log_level |
CHAPERONE_OBSERVABILITY_LOG_LEVEL |
string | info |
Log level: debug, info, warn, error |
enable_profiling |
CHAPERONE_OBSERVABILITY_ENABLE_PROFILING |
bool | false |
Enable /debug/pprof endpoints on the admin port |
enable_tracing |
CHAPERONE_OBSERVABILITY_ENABLE_TRACING |
bool | false |
Enable OpenTelemetry distributed tracing (see Tracing) |
| — | CHAPERONE_OBSERVABILITY_ENABLE_BODY_LOGGING |
bool | false |
Log request/response bodies at debug level. Env-var only — cannot be set in the YAML file (security safeguard). A startup warning is emitted when enabled. |
log_target_addr |
CHAPERONE_OBSERVABILITY_LOG_TARGET_ADDR |
string | host |
Detail level of the upstream target in the target_addr log field: host / path / full. See Target Address Logging. |
sensitive_headers |
— | []string | See below | Additional headers to redact (merged with defaults) |
The following headers are always redacted in logs, regardless of configuration:
AuthorizationProxy-AuthorizationCookieSet-CookieX-API-KeyX-Auth-Token
Any headers listed in sensitive_headers are merged with these defaults.
You cannot accidentally remove the built-in list by specifying custom headers —
this is a security safeguard.
# These are ADDED to the defaults, not replacing them
observability:
sensitive_headers:
- "X-Custom-Secret"
- "X-Vendor-Token"The log_target_addr setting controls how much of the upstream target URL
appears in the target_addr log field. The same value is applied uniformly
to every log line that references the target — request completed,
upstream response, allow-list events, plugin errors, and DEBUG breadcrumbs.
| Mode | Output example | Use it when |
|---|---|---|
host (default) |
api.vendor.com:8443 |
Always safe. Authority only — no scheme, no path, no query. |
path |
https://api.vendor.com:8443/v1/users |
You need to know which endpoint was called. Path appears; query is stripped. |
full |
https://api.vendor.com:8443/v1/users?key=val |
You need full request audit/debugging. Includes the query string. |
Userinfo (user:pass@host) is always stripped, in every mode.
Security:
pathandfullmay expose sensitive information. Path segments often carry IDs or PII (e.g.,/users/alice@example.com); query strings are a common location for tokens and API keys. Chaperone emits a startupWARNwhenfullis selected, and an informationalINFOwhenpathis selected.
For full per-request audit trails without leaking sensitive data into plaintext logs, prefer enabling OpenTelemetry tracing — spans include the full target URL but are exported through your observability pipeline rather than the local log stream.
OpenTelemetry distributed tracing is controlled by two independent mechanisms:
- Chaperone config —
enable_tracinginconfig.yamlorCHAPERONE_OBSERVABILITY_ENABLE_TRACINGenv var - OTel SDK — Standard
OTEL_*environment variables for exporter configuration
Tracing is only active when both layers are enabled. OTEL_SDK_DISABLED=true
always wins and disables the SDK regardless of the Chaperone config.
observability:
enable_tracing: true # Enable tracing in Chaperone# Configure the OTel exporter (standard OTel env vars)
export OTEL_EXPORTER_OTLP_ENDPOINT="http://collector:4318"
export OTEL_SERVICE_NAME="chaperone-prod" # Override default "chaperone"
export OTEL_TRACES_SAMPLER="parentbased_traceidratio" # Optional: sampling
export OTEL_TRACES_SAMPLER_ARG="0.1" # Optional: 10% sampling
# Emergency kill switch (always wins)
export OTEL_SDK_DISABLED=true| Variable | Purpose |
|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
OTLP collector endpoint |
OTEL_EXPORTER_OTLP_HEADERS |
Auth headers for the collector |
OTEL_SERVICE_NAME |
Override service name (default: chaperone) |
OTEL_RESOURCE_ATTRIBUTES |
Additional resource attributes |
OTEL_TRACES_SAMPLER |
Sampling strategy |
OTEL_TRACES_SAMPLER_ARG |
Sampler argument (e.g., ratio) |
OTEL_SDK_DISABLED |
Force-disable SDK (true always wins) |
Named upstreams that Chaperone can forward requests to instead of calling the vendor directly. Targets are referenced by name from a sdk.RouteAction returned by a RequestRouter. The contrib Mux implements RequestRouter and exposes targets through the forward: field on route entries.
When a router selects a forward target, the Core sends the request to that target's url (path and query included) with the configured authentication and timeout, and skips credential injection, ModifyResponse, and Core error normalization — the target's status code and body pass through to Connect verbatim. The request reaches the target at exactly the configured url; Chaperone's own ingress path is not appended, since the target routes on the X-Connect-* headers rather than the request path.
Inbound sensitive headers (Authorization, Cookie, etc.) are stripped before forwarding so Connect's auth posture cannot leak to the target. On the response side, only the built-in static sensitive-header list is stripped (as defense-in-depth against credential reflection); the operator-extended sensitive_headers list is not applied on the forward path, because no credential injection happens there and so there is nothing dynamic to reflect.
forward_targets:
customer-router:
url: "https://router.customer.example/v1/intake"
timeout: 15s
auth:
type: "bearer"
token: "${CUSTOMER_ROUTER_TOKEN}"
internal-relay:
url: "https://relay.internal.example/"
timeout: 10s
auth:
type: "none"| Key | Type | Default | Description |
|---|---|---|---|
url |
string | — (required) | Absolute base URL of the forward target. Must be https:// in production builds; http:// is permitted only in dev builds. |
timeout |
duration | 0 (use upstream defaults) |
Per-request timeout when calling the forward target. |
auth.type |
string | — (required) | bearer or none. Unknown values are rejected at startup. |
auth.token |
string | — | Bearer token used when auth.type: bearer. Required and must be non-empty for bearer auth. Supports ${VAR} and $VAR environment variable interpolation. |
Forward targets are validated at startup. The proxy fails fast with a descriptive error when any of these rules is violated:
urlmust be present, parseable, and have a non-empty scheme and host.- The scheme must be
httpsin production builds. In dev builds,httpis also accepted. auth.typemust be set; the empty string is rejected.auth.typemust bebearerornone; any other value is rejected.- When
auth.type: bearer,auth.tokenmust be non-empty after environment variable interpolation.
Routers that reference a forward_target name not defined here are also rejected at startup — see MuxConfig for how the contrib mux participates in this check.
The allow-list enforces a default-deny policy. Only requests matching
a host+path combination are forwarded to the vendor API. All other requests
receive a 403 Forbidden response.
Allow-list keys support optional port constraints:
host(no port): only the scheme default port is allowed (https→443,http→80)host:port: host and port must match exactly
This is a secure default that blocks non-standard ports unless explicitly allowed.
| Allow-list Key | Target URL | Result |
|---|---|---|
api.vendor.com |
https://api.vendor.com/v1/x |
Allowed |
api.vendor.com |
https://api.vendor.com:443/v1/x |
Allowed |
api.vendor.com |
https://api.vendor.com:8443/v1/x |
Denied |
api.vendor.com:8443 |
https://api.vendor.com:8443/v1/x |
Allowed |
api.vendor.com:8443 |
https://api.vendor.com/v1/x |
Denied |
localhost |
http://localhost:3000/test |
Denied |
localhost:8000 |
http://localhost:8000/test |
Allowed |
Note: http:// targets are only permitted when the binary is built with allowInsecureTargets=true; otherwise HTTPS is required.
Patterns work for both host keys (using . as segment separator) and
path values (using / as segment separator).
| Pattern | Matches | Example |
|---|---|---|
| Literal | Exact match only | Host: api.vendor.com, Path: /api/charge |
* |
Single segment | Host: *.vendor.com matches api.vendor.com but not a.b.vendor.com. Path: /v1/*/info matches /v1/users/info but not /v1/a/b/info |
** |
Zero or more segments (recursive) | Path: /v1/** matches /v1/, /v1/users, /v1/users/123/orders |
upstream:
allow_list:
# Exact host, recursive wildcard paths
"api.vendor.com":
- "/v1/**" # All paths under /v1/
- "/v2/products/*" # Single level under /v2/products/
# Exact host + explicit non-default port
"api.vendor.com:8443":
- "/v1/**"
# Domain glob — matches api.vendor.com, payments.vendor.com, etc.
"*.vendor.com":
- "/v1/**"
# Domain glob with explicit port
"*.vendor.com:9443":
- "/v1/**"
# Exact paths only
"payments.example.com":
- "/api/charge" # Only /api/charge (exact)
- "/api/refund" # Only /api/refund (exact)
# Mixed patterns
"storage.vendor.io":
- "/buckets/*/objects/**" # Objects within any bucketDenied requests are logged at warn level with the blocked host and path.
If a request is denied (403), check:
- The host in
X-Connect-Target-URLmatches an allow-list key (exact match is tried first, then glob patterns like*.vendor.com) - The path matches one of the patterns for that host
- There are no trailing slashes causing mismatches
| Scenario | Recommended Change |
|---|---|
| Slow vendor APIs (>10s response time) | Increase read to 60–120s |
| Large file uploads | Increase write to 120s+ |
| Plugin calls external secrets manager (Vault, AWS Secrets Manager) | Increase plugin to 15–30s |
| High connection churn | Decrease idle to 60s |
| Unstable network | Decrease connect to 3s, increase keep_alive to 10s |
| Graceful rolling deployment | Set shutdown_timeout ≥ max expected request duration |
- Lower
connect= Fail fast on unreachable hosts, better user experience - Higher
read/write= Tolerate slow vendors, but hold connections longer - Lower
idle= Free resources faster, but more TCP handshakes - Higher
plugin= Tolerate slow credential providers, but block request longer shutdown_timeoutshould be ≥ longest expected in-flight request
upstream:
timeouts:
connect: 3s # Fail fast
read: 60s # Vendor has slow endpoints
write: 60s # Large payloads
idle: 60s # Reclaim connections aggressively
keep_alive: 15s # Frequent probes
plugin: 15s # Vault calls may be slowSee configs/config.example.yaml for a
fully annotated configuration file with inline comments for every option.
- Deployment Guide — Deploy Chaperone in your environment
- Plugin Development Guide — Build your credential plugin
- Troubleshooting — Common issues and solutions