Skip to main content

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:

KeyDescription
modeVerification posture — see mode vocabulary below
ca_file / ca_dataPEM CA certificate, as a file path or base64-encoded inline data (mutually exclusive)
cert_file / cert_dataPEM client certificate for mTLS (mutually exclusive)
key_file / key_dataPEM client private key for mTLS (mutually exclusive)
server_nameTLS SNI / hostname override for certificate verification
trust_system_rootsAppend 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

ModeBehavior
disableNo TLS. Incompatible with an https:// URL or any cert/CA option.
requireTLS on, server certificate verification fully skipped. Requires AGENTVISOR_TLS_ALLOW_INSECURE=true — see below. Dev/test only.
verify-caVerify 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-fullFull 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

SubsystemConfig pathEnv var prefix
MPE HTTP policy providerauthz.http.tlsAGENTVISOR_AUTHZ_HTTP_TLS_*
Enrichment HTTP providerenrichment.definitions[].options.tls (and inline equivalents)n/a (YAML-only, dynamic provider options)
PostgreSQL storestore.postgresql.provider_options (tls_* keys)AGENTVISOR_STORE_POSTGRESQL_TLS_*
Temporal mTLStemporal.auth.mtlsAGENTVISOR_TEMPORAL_AUTH_MTLS_*
Trace gRPC sinktrace.sinks.grpc.tlsAGENTVISOR_TRACE_SINKS_GRPC_TLS_*
Trace webhook sinktrace.sinks.webhook.tlsAGENTVISOR_TRACE_SINKS_WEBHOOK_TLS_*
Egress proxy upstream TLSproxy.upstream_tlsAGENTVISOR_PROXY_UPSTREAM_TLS_*
MCP servers (sse/streamable_http)mcp.servers[].tlsn/a (nested array, no flat env var)
A2A agentsa2a_gateway.agents[].tlsn/a (nested array, no flat env var)
token_exchange resolvercredentials.tls (shared by proxy.credentials[], mcp.servers[].credentials, a2a_gateway.agents[].credentials)n/a (nested array, no flat env var)
OIDC JWKS fetchapi.auth.tlsAGENTVISOR_API_AUTH_TLS_*
OTLP tracing exportertelemetry.tracing.tlsAGENTVISOR_TELEMETRY_TRACING_TLS_*
Registry image pull/pushguest.registry_tlsAGENTVISOR_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.