TLS Trust Configuration
AgentVisor's host runtime makes outbound TLS connections to many different remote servers: the MPE policy decision point, an enrichment backend, a PostgreSQL store, Temporal Cloud, telemetry collectors, MCP servers, A2A agents, OAuth token endpoints, an OIDC provider, and arbitrary agent-directed destinations through the egress proxy. This page is the single reference for how each of those connections decides which certificate authorities to trust.
Scope
Covered: any outbound connection where AgentVisor is the TLS client and needs to decide which CA(s) to trust for the remote server.
Explicitly out of scope (different threat model):
- Inbound/listening TLS (
api.tls.*) — the API server presents its own identity to external callers and verifies inbound peers. This is a separate configuration surface from the outbound trust described here. - Ephemeral CA issuance — the guest's MITM CA (for HTTPS interception, see Credential Brokering) and the host↔guest mTLS credentials (see Architecture) are generated locally at startup with no operator-supplied trust anchor.
The tls configuration block
Most outbound TLS destinations in AgentVisor accept the same tls
configuration block:
| Key | Description |
|---|---|
mode | Verification posture — see mode vocabulary below |
ca_file / ca_data | PEM CA certificate, as a file path or base64-encoded inline data (mutually exclusive) |
cert_file / cert_data | PEM client certificate for mTLS (mutually exclusive) |
key_file / key_data | PEM client private key for mTLS (mutually exclusive) |
server_name | TLS SNI / hostname override for certificate verification |
trust_system_roots | Append the configured CA to the platform's system root pool instead of replacing it. Defaults to false everywhere except the egress proxy — see below. |
Mode vocabulary
| Mode | Behavior |
|---|---|
disable | No TLS. Incompatible with an https:// URL or any cert/CA option. |
require | TLS on, server certificate verification fully skipped. Requires AGENTVISOR_TLS_ALLOW_INSECURE=true — see below. Dev/test only. |
verify-ca | Verify the certificate chain against the configured CA; skip the hostname check. Requires ca_file or ca_data — a missing CA is a hard config error, not a silent fallback to the system pool. |
verify-full | Full verification: certificate chain and hostname. Default when a mode-bearing site is given an https:// URL (or, for non-URL clients like Postgres/Temporal, when any cert/CA option is set) with no explicit mode. |
The centralized insecure guard: AGENTVISOR_TLS_ALLOW_INSECURE
mode: require disables certificate verification entirely — the most
dangerous mode. AgentVisor refuses to establish such a connection unless
AGENTVISOR_TLS_ALLOW_INSECURE=true is set in the process environment. This
guard applies uniformly across every site listed below — set the env var once
to unlock mode: require anywhere it's configured.
Trust store: replace vs. append
trust_system_roots defaults to false everywhere except the egress proxy.
For a single-destination client (Temporal, a trace collector, one MCP server,
one A2A agent), replacing the trust store with just the operator's CA is
more secure than appending to the system pool: it pins that connection so a
mis-issued public-CA certificate for the same hostname can't MITM it. The
egress proxy is the deliberate exception — it reaches both private and
arbitrary public destinations through one shared client, so it defaults to
appending (true), while still allowing an operator to set it false to
lock the proxy to only its private CA.
Why the egress proxy exposes fewer options
A single-destination site (Temporal, a trace collector, one MCP server, one
A2A agent) accepts the full tls block — mode, CA, client cert, SNI all have
an unambiguous meaning because there's exactly one destination. The egress
proxy is different: it serves arbitrary agent-chosen destinations through
one shared client, so a client cert or SNI override "for the proxy" would be
silently applied to every destination an agent reaches — a different (and
wrong) semantic. The proxy therefore exposes only ca_file / ca_data /
trust_system_roots under proxy.upstream_tls, with no mode, no client
certs, and no server_name.
Where TLS trust is configured
| Subsystem | Config path | Env var prefix |
|---|---|---|
| MPE HTTP policy provider | authz.http.tls | AGENTVISOR_AUTHZ_HTTP_TLS_* |
| Enrichment HTTP provider | enrichment.definitions[].options.tls (and inline equivalents) | n/a (YAML-only, dynamic provider options) |
| PostgreSQL store | store.postgresql.provider_options (tls_* keys) | AGENTVISOR_STORE_POSTGRESQL_TLS_* |
| Temporal mTLS | temporal.auth.mtls | AGENTVISOR_TEMPORAL_AUTH_MTLS_* |
| Trace gRPC sink | trace.sinks.grpc.tls | AGENTVISOR_TRACE_SINKS_GRPC_TLS_* |
| Trace webhook sink | trace.sinks.webhook.tls | AGENTVISOR_TRACE_SINKS_WEBHOOK_TLS_* |
| Egress proxy upstream TLS | proxy.upstream_tls | AGENTVISOR_PROXY_UPSTREAM_TLS_* |
MCP servers (sse/streamable_http) | mcp.servers[].tls | n/a (nested array, no flat env var) |
| A2A agents | a2a_gateway.agents[].tls | n/a (nested array, no flat env var) |
token_exchange resolver | credentials.tls (shared by proxy.credentials[], mcp.servers[].credentials, a2a_gateway.agents[].credentials) | n/a (nested array, no flat env var) |
| OIDC JWKS fetch | api.auth.tls | AGENTVISOR_API_AUTH_TLS_* |
| OTLP tracing exporter | telemetry.tracing.tls | AGENTVISOR_TELEMETRY_TRACING_TLS_* |
| Registry image pull/push | guest.registry_tls | AGENTVISOR_GUEST_REGISTRY_TLS_* |
Per-subsystem reference
MPE HTTP policy provider
authz.http.tls.* / AGENTVISOR_AUTHZ_HTTP_TLS_* — full mode vocabulary,
ca_file/ca_data, client cert, server_name, plus trust_system_roots.
See the Configuration reference for the full
field list.
Enrichment HTTP provider
enrichment.definitions[].options.tls.* (central definitions) or the
equivalent inline enrichment.provider: http / options.tls block at a
per-site mcp.servers[].enrichment / a2a_gateway.agents[].enrichment — see
Authorization Enrichment. Full mode vocabulary,
ca_file/ca_data, client cert, server_name, and trust_system_roots.
PostgreSQL store
TLS options for store.postgresql are configured as flat keys inside the
generic store.postgresql.provider_options map: tls_mode, tls_ca_file,
tls_ca_data, tls_cert_file, tls_cert_data, tls_key_file,
tls_key_data, tls_server_name, tls_trust_system_roots (env:
AGENTVISOR_STORE_POSTGRESQL_TLS_*). Same four-value mode vocabulary as
above. The same key set applies to the PostgreSQL external checkpoint
storage provider's
temporal.checkpoint_storage.postgresql options.
Temporal mTLS
temporal.auth.mtls.* / AGENTVISOR_TEMPORAL_AUTH_MTLS_*: cert_file /
cert_data, key_file / key_data, ca_file / ca_data, server_name.
No mode field is exposed — mTLS is always required when
temporal.auth.type: mtls is set.
Trace gRPC sink
trace.sinks.grpc.tls.* / AGENTVISOR_TRACE_SINKS_GRPC_TLS_*: full mode
vocabulary, ca_file/ca_data, client cert, server_name. An unset/empty
mode (with no other TLS options configured) means TLS is off for this
sink — unlike the OTLP tracing exporter below, this isn't a special-cased
default; it's the ordinary default for a non-URL client with no TLS options
set.
Trace webhook sink
trace.sinks.webhook.tls.* / AGENTVISOR_TRACE_SINKS_WEBHOOK_TLS_*: full
mode vocabulary, ca_file/ca_data, client cert, server_name. This sink
has a real destination URL, so its scheme is cross-checked against mode as
usual (mode: disable + https:// is a config error).
Egress proxy upstream TLS
proxy.upstream_tls.* / AGENTVISOR_PROXY_UPSTREAM_TLS_*: the narrower
proxy-specific shape — ca_file / ca_data / trust_system_roots only, no
mode, no client certs, no server_name (see
Why the egress proxy exposes fewer options
above). trust_system_roots defaults to true — the one site in AgentVisor
where appending to (rather than replacing) the system root pool is the
default, since the proxy must reach both private and arbitrary public
destinations through the same shared client. Leaving proxy.upstream_tls
unconfigured is a no-op: proxied requests behave exactly as they would with
no TLS trust configuration at all, including HTTP/2 support and connection
pooling.
MCP servers
mcp.servers[].tls.* — full mode vocabulary, ca_file/ca_data, client
cert, server_name, under a tls block alongside each server's url. Only
valid for the sse/streamable_http transports; a tls block configured on
a stdio server is rejected as a config error at startup (stdio servers
have no URL/TLS concept). TLS and certificate-verification failures during
connection are reported with a message pointing at mcp.servers[].tls, so
they're easy to distinguish from other connection failures.
A2A agents
a2a_gateway.agents[].tls.* — full mode vocabulary, ca_file/ca_data,
client cert, server_name, under a tls block alongside each agent's url.
Applies to both unary and streaming requests to that agent.
token_exchange resolver
The token_exchange credential resolver's exchange_url (shared by
proxy.credentials[], mcp.servers[].credentials, and
a2a_gateway.agents[].credentials wherever type: token_exchange is
configured) accepts the same tls block — full mode vocabulary,
ca_file/ca_data, client cert, server_name — alongside exchange_url.
Configuring it once covers the token-exchange path for whichever gateway uses
it.
response_intercept does not need a tls block: it only inspects response
bodies from calls the proxy has already made through its own outbound path
and makes no outbound call of its own.
OIDC JWKS fetch
The OIDC authenticator's discovery document fetch (api.auth.oidc_issuer +
/.well-known/openid-configuration) and JWKS fetch (api.auth.oidc_jwks_url
override, or the discovered jwks_uri) share the same api.auth.tls block —
full mode vocabulary, ca_file/ca_data, client cert, server_name. Both
requests target the same OIDC provider, so one tls block covers both.
OTLP tracing exporter
telemetry.tracing.tls.* / AGENTVISOR_TELEMETRY_TRACING_TLS_*: full mode
vocabulary, ca_file/ca_data, client cert, server_name. Applies to both
the grpc and http OTLP protocols, and to the agent-span-forwarding path,
which shares the same tracing configuration.
One deliberate deviation from the other non-URL-client sites above: an
empty/unset mode here defaults to verify-full (TLS on) rather than the
usual "no TLS unless configured" default for a non-URL client like Temporal
or Postgres — a bare host:port endpoint with no URL scheme to infer trust
from is verified by default. Set mode: disable for a plaintext local
collector. When endpoint is a full URL (e.g. the Langfuse-style
https://cloud.langfuse.com/api/public/otel), the URL's own scheme is
cross-checked against mode as usual (mode: disable + https:// is a
config error).
Registry image pull/push
guest.registry_tls.* / AGENTVISOR_GUEST_REGISTRY_TLS_*: the same narrower
proxy-specific shape as the egress proxy — ca_file / ca_data /
trust_system_roots only, no mode, no client certs, no server_name (see
Why the egress proxy exposes fewer options
above). A registry client is a multi-destination client in the same sense as
the proxy — a single build can pull a base image from a public registry and
push the result to a private-CA registry through the same client — so
trust_system_roots defaults to true here as well.
Also exposes an insecure toggle (not present on the proxy's shape), which
maps to mode: require internally and is gated by the same
AGENTVISOR_TLS_ALLOW_INSECURE guard. insecure additionally allows
plain-HTTP registry references, not just skipped certificate verification.
Covers the guest image and mcp-tools image pulled by the host runtime
(guest.image, mcp.tools_image), and the agentvisor build/build guest
CLI commands (base image pull, result push) via the equivalent
--registry-ca-file / --registry-ca-data / --registry-insecure flags,
also available on run/serve/exec. Registry authentication (as opposed
to TLS trust) is configured separately via guest.registry_auth — see
Registry Authentication.