Skip to content
GitHubOpen source
Product
Docs
Solutions
IndustriesAll industries
CompareCompare all AI data-leak tools
Benchmarks
GitHubApache-2.0

Proxy mode

In proxy mode, tools send their traffic to Shadow with the standard HTTPS_PROXY setting. For each connection, Shadow looks at the destination host:

  • Known AI host (for example api.openai.com): Shadow terminates TLS with a certificate from its local CA, masks the request, forwards it to the real host over a fresh TLS connection, and restores the response.
  • Any other host: Shadow opens a plain TCP tunnel and copies bytes in both directions. It never sees the content, because it never holds a certificate for that host.

TLS is terminated only on your machine, before the request is encrypted again for the provider.

Terminal window
shadow run # foreground
# or, as a background service with the CA trusted:
shadow install --apply

shadow init creates a CA at <data dir>/ca/ca.pem. Its private key is created with mode 0600 and never leaves the machine. The CA is constrained (path length 0) and valid for 10 years. Leaf certificates are minted in memory per host.

shadow install --apply adds the CA to the OS trust store. To do it by hand:

Terminal window
security add-trusted-cert -r trustRoot \
-k ~/Library/Keychains/login.keychain-db \
~/Library/Application\ Support/shadow/ca/ca.pem

The running proxy also serves the CA at http://127.0.0.1:8877/shadow/ca.pem, which is handy in containers.

Several runtimes ignore the OS trust store. Point them at Shadow’s files:

RuntimeVariableValue
Node.jsNODE_EXTRA_CA_CERTSca/ca.pem (adds to the built-in roots)
Python requestsREQUESTS_CA_BUNDLEca/bundle.pem
Python httpx, OpenSSL-based toolsSSL_CERT_FILEca/bundle.pem
curlCURL_CA_BUNDLEca/bundle.pem

bundle.pem contains the system roots plus the Shadow CA, because these variables replace the trust store rather than extend it.

shadow install prints the exact lines for your data directory, and --apply writes them to <data dir>/env.sh (or env.ps1):

Terminal window
export HTTPS_PROXY=http://127.0.0.1:8877
export NO_PROXY=localhost,127.0.0.1
export NODE_EXTRA_CA_CERTS="$HOME/Library/Application Support/shadow/ca/ca.pem"
export REQUESTS_CA_BUNDLE="$HOME/Library/Application Support/shadow/ca/bundle.pem"
export SSL_CERT_FILE="$HOME/Library/Application Support/shadow/ca/bundle.pem"
export CURL_CA_BUNDLE="$HOME/Library/Application Support/shadow/ca/bundle.pem"

On Linux the paths are under ~/.local/share/shadow.

Add source "<data dir>/env.sh" to ~/.zshrc or ~/.bashrc to make it permanent.

Apps that use the OS proxy settings (browsers, many desktop apps) can use the PAC file Shadow serves at /proxy.pac. It sends only the intercepted hosts through Shadow and everything else direct. In a browser that means AI web apps such as chatgpt.com, claude.ai and gemini.google.com go direct and are not masked:

Terminal window
# macOS
networksetup -setautoproxyurl "Wi-Fi" http://127.0.0.1:8877/proxy.pac

On Windows, set it under Settings → Network → Proxy → Use setup script. On GNOME, under Settings → Network → Network Proxy → Automatic.

Run shadow hosts to list the patterns in effect. The built-in list covers OpenAI, Anthropic, OpenRouter, Google Gemini and Vertex AI, Azure OpenAI and Azure AI services, Mistral, Groq, Together, DeepSeek, xAI, AWS Bedrock runtime, Cohere, Perplexity, Fireworks, Cerebras and Voyage AI. The full list is in Supported providers.

Add hosts, such as a self-hosted LLM gateway, with extra_hosts. Exclude hosts with bypass_hosts. Both accept * globs:

[proxy]
extra_hosts = ["llm.internal.example.com", "*.inference.example.net"]
bypass_hosts = ["api.cohere.com"]

A bypassed host is tunnelled like any non-AI host: never decrypted, never masked.

Absolute-form HTTP requests (http://host/... sent to the proxy) are forwarded too. They are masked only if the host is on the intercept list.

Translation hides content, not your IP address. To make AI traffic leave from somewhere else, for example a relay in the persona’s region, set an upstream proxy. It applies to intercepted traffic only:

[proxy]
upstream_proxy = "socks5h://relay.example.net:1080" # http://, socks5:// or socks5h://

If Shadow is stopped, tools configured with HTTPS_PROXY fail to connect. To bypass it temporarily, unset the variable for one command:

Terminal window
env -u HTTPS_PROXY your-tool