Skip to main content

HTTP Proxy

AgentVisor™'s HTTP proxy transparently intercepts all outbound HTTP requests from agents, enabling policy enforcement without code changes.

How It Works

1. Agent code makes HTTP request
│ response = requests.get("https://api.openai.com/v1/models")


2. Request intercepted by HTTPS_PROXY
│ → http://localhost:<dynamic-port>
│ → CONNECT api.openai.com:443


3. Guest proxy terminates TLS
│ • Ephemeral CA signs per-host certificate
│ • TLS handshake with agent's HTTP client
│ • Decrypted request headers and body now visible


4. Guest Runtime securely forwards decrypted request to Host
│ Over the private host channel (gRPC over UDS or TCP+mTLS)


5. Host Runtime processes request

├─▶ 5a. Policy Check (MPE)
│ • Resource: mrn:agentvisor:http:api.openai.com/v1/models
│ • Operation: GET
│ • Principal: from context
│ │
│ ├─ ALLOWED → continue
│ └─ DENIED → return 403

├─▶ 5b. Audit Log
│ • Record request details
│ • Include policy decision

├─▶ 5c. Credential Substitution
│ • Replace symbolic tokens (mav-tok-...) with real credentials

└─▶ 5d. Execute Request
• Make actual HTTPS request to upstream
• Return response to guest

Transparent Proxying

The proxy is transparent - no code changes required:

# These all work automatically:

import requests
response = requests.get("https://api.openai.com/v1/models")

import httpx
async with httpx.AsyncClient() as client:
response = await client.get("https://api.anthropic.com/v1/complete")

from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(model="claude-sonnet-4-20250514")
response = llm.invoke("Hello!")

How Transparency Works

The guest runtime sets environment variables:

HTTP_PROXY=http://localhost:12345
HTTPS_PROXY=http://localhost:12345
NO_PROXY=localhost,127.0.0.1
note

Python's requests, httpx, urllib3, and aiohttp all honor these variables automatically.

HTTPS and TLS Termination

For HTTPS requests, the guest proxy terminates TLS using an ephemeral Certificate Authority. This enables the proxy to inspect request headers and bodies, which is required for credential brokering and full policy enforcement.

1. Client sends CONNECT api.openai.com:443
2. Proxy accepts the CONNECT tunnel
3. Proxy performs TLS handshake with the client
• Uses ephemeral CA to sign a certificate for api.openai.com
• Client trusts this cert via SSL_CERT_FILE CA bundle
4. Decrypted HTTP request is read from the TLS connection
5. Request securely forwarded to host over the hostlink session (gRPC over UDS or TCP+mTLS, depending on sandbox mode)
6. Host performs policy check, credential substitution, and upstream HTTPS request

This means:

  • The proxy can inspect all request headers and body content
  • Symbolic credential tokens are visible for credential brokering
  • Full request details are available for policy enforcement and audit logging
  • Agent HTTP libraries trust the proxy CA transparently via SSL_CERT_FILE

Per-host certificates are generated on demand and cached (1-hour validity). The ephemeral CA is regenerated on each guest restart (30-day validity, configurable via AGENTVISOR_GUEST_CA_VALIDITY). No code changes are needed in the agent.

Streaming Support

The proxy supports SSE (Server-Sent Events) for LLM streaming:

from langchain_anthropic import ChatAnthropic

llm = ChatAnthropic(model="claude-sonnet-4-20250514", streaming=True)

for chunk in llm.stream("Tell me a story"):
print(chunk.content, end="", flush=True)

How Streaming Works

1. LLM request with stream=true


2. Guest Runtime forwards the streaming request to Host


3. Host makes request, receives SSE stream


4. Host streams chunks back over the private host channel
│ Each chunk carries response data plus a done flag (and an
│ optional error if the upstream stream fails mid-flight)


5. Guest Runtime converts to HTTP chunked response


6. Python receives streaming response

Policy Integration

Every request is evaluated against the policy:

MRN Format

HTTP requests are converted to MRNs:

mrn:agentvisor:http:<host><path>

Examples:
• https://api.openai.com/v1/chat/completions
→ mrn:agentvisor:http:api.openai.com/v1/chat/completions

• http://httpbin.org/get?foo=bar
→ mrn:agentvisor:http:httpbin.org/get

Policy Example

resources:
# Allow OpenAI API
- name: openai
selector:
- "mrn:agentvisor:http:api\\.openai\\.com.*"
group: "mrn:iam:resource-group:allowed"

# Block internal networks
- name: internal
selector:
- "mrn:agentvisor:http:10\\..*"
- "mrn:agentvisor:http:192\\.168\\..*"
- "mrn:agentvisor:http:172\\.(1[6-9]|2[0-9]|3[0-1])\\..*"
group: "mrn:iam:resource-group:denied"

# Default deny
- name: default-http
selector:
- "mrn:agentvisor:http:.*"
group: "mrn:iam:resource-group:denied"

Configuration

Timeouts

# Request timeout (default 5m)
export AGENTVISOR_PROXY_REQUEST_TIMEOUT=30s

# Max body size (default 10MB)
export AGENTVISOR_PROXY_MAX_BODY_SIZE=10485760

Troubleshooting

Request Blocked

If a request is denied, check:

  1. Policy configuration: Does the resource match an allow rule?
  2. MRN format: Is the selector pattern correct?
  3. Policy order: Deny rules may match before allow rules

Enable debug logging:

AGENTVISOR_LOG_LEVEL=debug agentvisor serve ...

Timeouts

For long-running LLM requests:

export AGENTVISOR_PROXY_REQUEST_TIMEOUT=120s

SSL/TLS Errors

SSL errors in the sandbox typically indicate a CA bundle issue:

  1. SSL_CERT_FILE not set correctly: The guest runtime automatically sets SSL_CERT_FILE to a combined CA bundle containing both system CAs and the ephemeral proxy CA. If this variable is overridden, Python libraries won't trust the proxy.
  2. Custom CA requirements: If your agent explicitly sets certificate paths, ensure the combined CA bundle is used instead.
  3. CA regeneration: The ephemeral CA regenerates on each restart. Agents pick this up automatically through the CA bundle file.

Enable debug logging to see TLS-related issues:

AGENTVISOR_LOG_LEVEL=debug agentvisor serve ...

Bypassing the Proxy

warning

Bypassing the proxy removes all policy protection and credential brokering!

Only when running with --sandbox=none, you can set NO_PROXY to bypass specific hosts:

import os
os.environ["NO_PROXY"] = "localhost,127.0.0.1,internal.example.com"

In gVisor and Docker (container) modes, the proxy cannot be bypassed: either there is no network interface at all (gVisor, Docker on Linux), or outbound traffic is restricted by in-guest nftables to the hostlink address only (Docker on macOS/Windows). Either way, the proxy is the only route out.