Cloudveil Blocking LLM API Calls: Symptoms, Causes, and Workaround

The below text was written by gemini. I am publishing this because I ran into issues vibe coding a chatbot on a Mac with Cloudveil filter installed and want to allow other uses/llm agents to find this helpful.

This document outlines an issue where Python scripts and local containers fail to connect to LLM APIs due to local content-filtering software, along with the diagnostic steps and a practical workaround.

Symptom

Python-based tools on macOS (such as chat.py or scripts utilizing requests and urllib) fail to reach API endpoints including api.deepseek.comapi.openai.com, and api.anthropic.com. The scripts output the following error:

Plaintext

SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
self-signed certificate in certificate chain

This failure also applies to traffic originating from inside Docker containers, as Docker on macOS routes its traffic through the host’s network path.

Root Cause

The root cause of the error is Cloudveil, a content-filtering application installed on the machine. Cloudveil performs transparent TLS interception at the network and process level, rather than relying on a standard HTTP or HTTPS proxy configuration. Command-line checks using scutil --proxy or environment variable inspections confirm no proxy is configured.

Cloudveil applies this interception based on the specific requesting process:

  • Trusted processes: System binaries such as the browser, the native curl, and the notarized Claude Code CLI are permitted to pass through without interception. Accessing the API domains directly via a web browser successfully returns a standard 200 OK JSON response.
  • Unrecognized processes: Network traffic initiated by python3 scripts or Docker containers is intercepted. Because Cloudveil cannot generate a valid certificate for the requested domain on behalf of an untrusted client, it serves a fake certificate (issuer: O=Cloudveil Filtering Certificatesubject: O=GoProxy untrusted MITM proxy Inc). Python’s native TLS stack correctly rejects this certificate because it is not present in any trusted Certificate Authority (CA) store.

This behavior is not a domain-level block. The actual AI chat interfaces load successfully in the browser, indicating that only non-browser and non-signed application traffic is being targeted by the interception.

Diagnostic Commands

To verify if this specific process-level interception mechanism is causing connection issues on a new domain or tool, execute the following commands:

Bash

# Compare the certificate seen by an untrusted TLS probe vs. a trusted curl client
echo | openssl s_client -connect <host>:443 -servername <host> 2>/dev/null \
  | openssl x509 -noout -issuer -subject

curl -sv "https://<host>" --max-time 8 -o /dev/null 2>&1 \
  | grep -i -E "issuer|subject:|SSL certificate"

# Confirm that no standard proxy is configured on the system
scutil --proxy
env | grep -i proxy

If the openssl command returns the Cloudveil MITM certificate while curl returns the actual CA for the same host, process-level interception is actively modifying the traffic.

Workaround and Fix

Subprocess curl Implementation

A functional workaround is to modify the Python script to bypass the requests library and instead execute the system’s trusted curl binary via subprocess.Popen. Because curl is recognized as a trusted process by Cloudveil, the traffic is passed through successfully.

To implement this securely:

  • API Key Security: Supply the API key using a curl -K config file containing the authorization header (header = "Authorization: Bearer ..."). Avoid using the -H command-line argument, which would expose the key in ps auxoutput to other local users. Set the configuration file permissions to chmod 600 and delete the file immediately upon request completion.
  • Request Body: Stream the JSON payload directly to curl over stdin (data-binary = @-) to avoid shell execution and argument-length constraints.
  • Streaming Responses: Parse Server-Sent Events (SSE) responses line-by-line from the stdout stream of the curlsubprocess.

Permanent Resolution

For a permanent fix, the Cloudveil configuration must be modified by an administrator. This requires either allowlisting the specific AI API domains (api.deepseek.comapi.openai.comapi.anthropic.com) or broadly exempting Python and script-based traffic from interception.