Outbound callout security

Praxis AI treats an outbound target, its resolved socket addresses, and the credentials attached to the request as one trust decision.
On this page

Praxis AI treats an outbound target, its resolved socket addresses, and the credentials attached to the request as one trust decision. Operator-configured HTTP targets are public-only by default. Each request resolves its target once, checks every returned address immediately before connecting, and gives those same addresses to the transport. A single private, loopback, link-local, or otherwise non-public result rejects the whole callout.

Private targets require a filter-specific, explicit opt-in. HTTP proxies and redirect following are disabled for direct callouts. URL userinfo is rejected, and forwarded or configured credentials remain bound to the validated target origin.

Callout inventory

No-follow means a redirect response is returned to the caller and its Location is never requested.

CalloutTarget sourcePrivate opt-inRedirect policyAuthentication mode
openai_web_search, anthropic_web_searchProvider default or configured base_url, executed through the outbound_chain filtered-subrequest executorinsecure_options.allow_private_upstreams (executor-gated on the bound outbound chain)No-followConfigured provider API key; validated origin only
openai_file_resolve Files APIConfigured files_api_url via outbound_chainallow_private_upstreamsNo-followHeaders named by forward_headers plus outbound_chain mutations
openai_file_resolve file_url fetchRequest-derived URL via SubRequestClientExact allowed_file_url_originsNo-followAnonymous; no downstream headers
openai_file_search_calloutConfigured vector_store_urlallow_private_urlNo-followOnly headers named by forward_headers
openai_responses_compactConfigured inference_urlallow_private_inference_urlNo-followAnonymous; no downstream or cluster headers
ai_guardrails with NeMoConfigured endpointGlobal allow_private_upstreamsNo-followConfigured outbound_chain; no downstream headers by default
http_calloutConfigured target.urlallow_private_addressesNo-followConfigured static headers plus allowed forward_headers
external_metering balance check and usage reportConfigured metering_url via SubRequestClientallow_private_endpointNo-followAnonymous; no downstream headers
MCP clientRequest-derived server URL or configured connectorallow_loopback for loopback onlyNo-followSanitized request-provided MCP authorization/headers
azure_ad token fetchConfigured authority plus tenant via SubRequestClientallow_private_authorityNo-followClient secret in the token POST body
gcp_adc metadata fetchProtocol-owned metadata endpoint via SubRequestClientIntrinsic to metadata modeNo-followMetadata-Flavor protocol header; returned token is not forwarded back to metadata

The request-derived file_url and MCP transports retain stricter policies: they resolve once per attempt, validate every address against AddressPolicy, do not follow redirects, and allow private access only through their narrow origin/loopback controls. GCP metadata intentionally allows its protocol-owned private destination via AddressPolicy::AllowPrivate.

external_metering calls out twice per request, so it resolves its configured host through the upstream DNS cache rather than on every call. Every cached address is still validated against AddressPolicy before connecting.

Upstream cluster connections are not direct callouts. They use the core Praxis endpoint and TLS policy instead of these filter-level controls.

Adding a callout

A new filter that opens an outbound HTTP connection must:

  1. Classify the target as operator-configured, request-derived, or protocol-owned.
  2. Reuse the shared target/address policy, or document why a stricter dedicated policy is required.
  3. Default to public addresses and expose a narrowly named private-address opt-in only when the use case requires it.
  4. Resolve once per connection attempt, reject the complete result set if any address violates policy, and connect only to that validated set.
  5. Disable ambient proxies and redirects unless their security behavior is explicitly designed and tested.
  6. Reject URL userinfo and bind every credential or forwarded header to the validated origin.
  7. Document the authentication mode and cover loopback/private/link-local, mixed DNS answers, redirects, userinfo, and credential non-disclosure in tests.

TLS backend

Production outbound callouts use SubRequestClient, which inherits TLS from the process-wide Pingora/praxis-tls provider installed at startup (praxis_tls::provider::install). No per-callout or per-filter TLS backend selection is needed — all callouts share the same validated TLS stack as upstream cluster connections.

On RHEL/OpenShift FIPS builds, the system OpenSSL provider enforces approved cipher suites and algorithms for every outbound connection automatically. The FIPS feature set includes openai-file-resolve-filter and aws-sigv4-filter; openai-mcp-tools remains excluded because its store dependency pulls sha2 through sqlx, and the response stores and policy engine are excluded for the same reason.

reqwest is a dev/test-only dependency. Test TLS infrastructure (tests/utils/src/net/tls.rs) uses rustls for mock servers and certificate generation, independent of the production TLS backend.