Endpoint override on Windows and Linux
Route an authorized local client's fixed HTTPS endpoint through mitmproxy, with scoped capture, TLS trust, streaming and rollback.
Use a client's built-in Base URL / Custom endpoint / BYOK setting whenever possible. See Connection guides and GitHub Copilot. Local HTTPS interception is an optional fallback for a client that cannot change its endpoint, not a requirement for using Model Gate.
This is not universally compatible. The client must send a protocol, path and model supported by your Model Gate account, accept your local certificate authority, and allow you to configure a Model Gate API key. Certificate pinning, a private trust store, signed requests, a hard-coded model, or a remote extension host can prevent this method from working. A URL rewrite does not convert OpenAI Chat Completions into Anthropic Messages or Responses, add model capabilities, or replace GitHub authentication.
Only intercept applications and traffic you own or are authorized to inspect. Obtain approval on managed workstations. mitmproxy sees decrypted prompts, source code and authentication headers; its local CA private key can impersonate HTTPS servers to clients that trust it. Do not share CA private keys, export unredacted flows/HAR files, expose its UI to the network, or disable TLS verification.
What this example changes
The example maps exactly this source origin to the Model API origin shown in this site's documentation:
https://api.deepseek.com/v1/chat/completions
-> local mitmproxy
-> https://api.model-gate.com/v1/chat/completions
Configure a dedicated Model Gate API key, not a DeepSeek key, in the client's credential field. Choose an enabled Model Gate model or administrator-managed alias from the model catalog. Use the Model API origin shown in this guide with its matching API key; do not use the panel or Partner API host.
The rule does not intentionally edit the HTTP method, path, query, JSON body, model, Authorization, or the JSON stream value. It does change the destination and HTTP Host / HTTP/2 :authority; upstream TLS connects to the new destination. HTTP version, header casing/order, connection headers and framing can also differ. It is not byte-for-byte preservation of all headers or network traffic.
Prepare response-only streaming (both systems)
Save the following as model-gate-response-stream.py in your working directory. The same file is included in the release inside the PHP application at deploy/client-tools/; it runs inside mitmproxy and needs no separate Python installation when using the native package.
"""Keep map_remote requests buffered; stream responses without retaining bodies."""
from mitmproxy import ctx, exceptions, http
def configure(updated: set[str]) -> None:
if ctx.options.stream_large_bodies is not None:
raise exceptions.OptionsError(
"Unset stream_large_bodies: map_remote must run before request forwarding."
)
def requestheaders(flow: http.HTTPFlow) -> None:
flow.request.stream = False
def responseheaders(flow: http.HTTPFlow) -> None:
if flow.response is not None:
flow.response.stream = True
The built-in map_remote addon rewrites the destination in the request hook, after the request body is read. Keep request streaming disabled until that rewrite has happened. Do not use --set stream_large_bodies=1 with this recipe: it can forward the original request before the rewrite. The small addon above enables only response streaming, including SSE, without modifying the JSON body or replaying requests.
The commands below use --set stream_large_bodies without an equals sign or value to reset this optional setting to None, and explicitly disable streamed-body retention. The addon rejects enabling a global streaming threshold later. Remove other request-streaming/rewrite addons from this dedicated session, and resolve any startup errors before placing a key in the client. The proxy is not a fail-closed credential boundary: validate the route with a dummy key first.
Windows: Visual Studio example
Install the official native Windows package from mitmproxy Downloads. Reopen PowerShell and check mitmweb --version. Do not install inside WSL for this Windows-process example.
Create and trust this installation's local CA
Start once under the same Windows account that will run the proxy:
mitmweb --listen-host 127.0.0.1 --web-host 127.0.0.1
After startup, stop it with Ctrl+C. mitmproxy creates its CA in %USERPROFILE%\.mitmproxy. Prefer Current User trust for an application running as you:
$ca = Join-Path $env:USERPROFILE '.mitmproxy\mitmproxy-ca-cert.cer'
if (-not (Test-Path -LiteralPath $ca)) { throw 'Start mitmweb once under this account first.' }
certutil -user -addstore Root "$ca"
if ($LASTEXITCODE -ne 0) { throw 'CA installation failed.' }
Trust only the CA generated by your own installation. mitmproxy-ca-cert.cer is the public certificate; mitmproxy-ca.pem also contains the private key and must remain private. Restart Visual Studio after changing trust.
Only when an approved application specifically needs Local Machine trust, use an elevated PowerShell and certutil -addstore Root "$ca" without -user. This trusts the CA machine-wide, is a broader security change, and requires the matching machine-store removal below. Elevation under a different administrator account may use a different profile and CA; keep the account, certificate and proxy configuration consistent.
Capture only the intended process and host
Run native PowerShell as Administrator when required by Windows capture permissions, using the same account/profile. Start Visual Studio first. This example targets the process name devenv:
mitmweb --mode local:devenv `
--listen-host 127.0.0.1 --web-host 127.0.0.1 `
--allow-hosts '^api\.deepseek\.com(:443)?$' `
--set connection_strategy=lazy --set upstream_cert=false `
--set stream_large_bodies --set store_streamed_bodies=false `
-s .\model-gate-response-stream.py `
--set 'map_remote=|^https://api\.deepseek\.com(?::443)?/|https://api.model-gate.com/'
There must be no spaces after PowerShell's line-continuation backtick. The anchored rule matches only the specified HTTPS origin, including an explicit port 443; a provider hostname occurring inside a query or another hostname is not rewritten. The host allow-list avoids decrypting unrelated destinations from the selected process. lazy plus upstream_cert=false avoids an unnecessary certificate-sniffing connection to the original provider. TLS verification of the actual upstream is still enabled.
Visual Studio is not VS Code. An extension may send requests through a separate ServiceHub, language-server or helper process rather than devenv. Identify the actual network-owning process before broadening capture. To inspect Visual Studio instances:
Get-Process -Name devenv | Select-Object Id, ProcessName, Path
Replace local:devenv with local:12345 using the actual PID, or a comma-separated list of explicitly identified PIDs. PIDs change after restarts. Do not use $PID for a custom PowerShell variable; it refers to PowerShell's own process. Do not switch to whole-machine --mode local merely to make a missing request appear.
Linux: the same scoped mapping
Install a current official mitmproxy build and check mitmweb --version and uname -r. Local Capture uses eBPF; the official support floor is Linux 6.8. It needs a privileged helper started through sudo. Run mitmweb as your ordinary user with --mode local:... on the command line so it can request that privilege; avoid switching the entire proxy to root and accidentally using /root/.mitmproxy.
First initialize the CA under your ordinary account, then stop with Ctrl+C:
mitmweb --listen-host 127.0.0.1 --web-host 127.0.0.1
Prefer the application's documented custom-CA mechanism. On Ubuntu/Debian, applications using the system trust store can instead use this optional, system-wide installation:
sudo install -m 0644 "$HOME/.mitmproxy/mitmproxy-ca-cert.pem" \
/usr/local/share/ca-certificates/model-gate-local-mitmproxy.crt
sudo update-ca-certificates
Restart the client. A private Java/Node/browser trust store or a confined application may need its own documented trust setup; importing into the OS store does not guarantee that every client trusts it. Other distributions use their own CA-store procedures.
For a local VS Code process named code, the mapping is:
mitmweb --mode local:code \
--listen-host 127.0.0.1 --web-host 127.0.0.1 \
--allow-hosts '^api\.deepseek\.com(:443)?$' \
--set connection_strategy=lazy --set upstream_cert=false \
--set stream_large_bodies --set store_streamed_bodies=false \
-s ./model-gate-response-stream.py \
--set 'map_remote=|^https://api\.deepseek\.com(?::443)?/|https://api.model-gate.com/'
As on Windows, use the actual request-producing process/PID, not necessarily the editor window. Inspect ps -eo pid,comm,args and substitute local:12345 as needed. Linux name matching is limited to the first 16 characters. Local Capture on WSL is unsupported, and containers need host networking for this mode. Traffic from an SSH/remote extension host must be configured on the machine where that process runs, not just on your desktop.
Streaming, paths and other providers
The response-only addon forwards HTTP responses without waiting for the complete response, which is important for SSE token delivery. Request bodies remain buffered until map_remote has changed the destination. Streaming bodies are not retained for inspection by default; headers and status remain useful. Do not enable body retention or flow export merely to troubleshoot a key. This setting does not change the client's JSON stream flag and cannot make a non-streaming provider stream.
The default mapping preserves paths. A client sending /chat/completions without /v1 will still send that path to Model Gate and may receive 404. Only for a source API whose paths are known to require this normalization, replace the map_remote value with:
map_remote=|^https://api\.deepseek\.com(?::443)?/(?:v1/)?|https://api.model-gate.com/v1/
This deliberately adds one /v1/ prefix, preserving an existing one, the remaining path and query. It is a separate path-changing variant, not the origin-only example. Verify the resulting endpoint before sending a real prompt.
For a different provider, replace the exact source hostname in both allow-hosts and map_remote, escape regex dots, and choose the correct target origin/path. Keep the match anchored with ^https:// and a hostname boundary; never use a broad substring replacement. Existing saved addons or rewrite rules can alter the result, so inspect your mitmproxy configuration before testing.
Acceptance and troubleshooting
First test routing with a dummy credential and nonsensitive prompt; an authentication failure at the intended target is expected. Only after the destination is confirmed, make one deliberately small request with a dedicated limited key; inference can be billable. In mitmweb, check that the destination is the intended Model Gate API host, the path is supported, the model ID/alias exists and the HTTP status is successful. Check Model Gate Request History and confirm that streamed text arrives progressively. Do not publish an Authorization header or a flow export as evidence.
No captured request. The actual helper/PID, capture privileges, local versus remote execution, source hostname and kernel support.
TLS certificate failure. Correct CA/profile/store and client restart. Certificate pinning is a compatibility limitation, not a reason to disable verification.
401. The client must use a Model Gate key for the matching account/API domain; map_remote does not exchange credentials.
404. Inspect the actual path; origin-only mapping does not add /v1.
400 or messages.0 / system error. Check request-protocol compatibility. An origin rewrite does not translate message roles or other JSON fields.
Model unavailable. Use an enabled canonical ID or existing alias; mapping does not rename model.
Streaming arrives all at once. Check that the response-only addon loaded successfully, the client's stream value and actual provider support. Do not enable global request streaming.
Stop and remove trust
Before stopping interception, close the client or remove the Model Gate key from its original-provider configuration. Otherwise its next direct request can send that key to the original provider. Do not assume that stopping the proxy fails closed. Stop mitmweb with Ctrl+C, then restore the client's normal endpoint/key settings.
For the Windows Current User import above, remove only this installation's exact certificate:
$ca = Join-Path $env:USERPROFILE '.mitmproxy\mitmproxy-ca-cert.cer'
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($ca)
certutil -user -delstore Root $cert.Thumbprint
if ($LASTEXITCODE -ne 0) { throw 'CA removal failed; inspect the certificate store.' }
For the optional Local Machine import, use an elevated shell and certutil -delstore Root $cert.Thumbprint instead. Keep the public CA file until you have identified and removed the matching trusted certificate; do not delete unrelated trusted roots by name.
For the Ubuntu/Debian system-wide import above:
sudo rm -- /usr/local/share/ca-certificates/model-gate-local-mitmproxy.crt
sudo update-ca-certificates --fresh
Remove any application-specific CA configuration too. Restart clients. Delete sensitive captured artifacts and rotate the dedicated Model Gate key if it was exposed or sent to an unintended destination. These workstation steps do not require changing nginx, server trust stores, production .env files or Model Gate's TLS policy.
Official references
Reviewed on 2026-09-08. Confirm options against your installed mitmweb --options; client capture and trust behavior still require testing on your workstation.
- mitmproxy Local Capture modes and limitations
- mitmproxy URL mapping and streaming
- mitmproxy response-streaming addon example
- mitmproxy map_remote request-hook implementation
- mitmproxy options
- mitmproxy CA certificates and pinning
- mitmproxy HTTP request destination and authority
- Microsoft certutil
- Ubuntu CA installation and removal