Network inspection behind a VPN
In this option, agents need no gateway settings: each laptop only has to trust the company's inspection certificate. Each laptop runs your company VPN to a gateway host you run. There, a TLS inspection proxy and Shield check every response from the model provider before it goes back down the tunnel. Once the VPN is connected, enforcement is automatic for an agent the gateway covers: today Claude Code, on Anthropic's API (see which agents it covers). Such an agent keeps its default provider URL. Setting NO_PROXY, or pointing the agent at another hostname or a bare IP on Anthropic's address, does not get around the gateway: the API is inspected and anything else there is refused, apart from Anthropic's named websites. Which other traffic goes through the VPN depends on the routing design.
The design fits any VPN and inspection proxy that meet the requirements below. Our reference deployment is WireGuard, an iptables firewall and mitmproxy on a Linux gateway host, and it is the only one we have tested. The steps, the identity table and the test results on this page are that deployment's.
Tested on the reference deployment, with Claude Code 2.1.296, mitmproxy 12.2.3 and WireGuard, against a mock of the Anthropic Messages API. Linux containers stood in for the Mac and the gateway host. macOS-specific behaviour, the live provider and your own network, VPN and proxy are what a pilot validates. Read what changes for browsing before you plan a rollout.

Zoomable version: open the interactive gateway architecture.
How it works
- Each Mac has its own VPN credential and a tunnel address bound to it, registered by an administrator with the user and device it belongs to. In the reference deployment the credential is a WireGuard key, and a key the gateway does not know cannot complete a handshake.
- The gateway firewall accepts provider traffic only. Connections to approved provider addresses on port 443 are redirected into the inspection proxy. Other forwarding is rejected, and so are other ports, UDP 443 and IPv6.
- The proxy terminates TLS with a company inspection certificate that the agents trust, and opens its own verified TLS connection to the provider. Which connections it decrypts depends on the routing design. The full tunnel decrypts every hostname on the provider's address and refuses all but the API. The daily-use design passes Anthropic's named websites through undecrypted, decrypts the API, and decrypts and refuses anything else.
- The proxy holds the provider's whole response and asks Shield about every tool call in it, with the same mapping as the AgentShield hook. It releases the original response only if every call is approved. Otherwise it returns an HTTP 400 that names the rule.
- Shield records who sent it. The proxy looks up the enrolled user and device for the tunnel address the request arrived on. The tunnel already authenticated that address with the device's VPN credential. Shield writes the decision, with that identity, to a hash-chained audit log on the gateway. In the pilot that write is best-effort: if the log cannot be written, Shield still answers, so monitor it.
Only the provider's Messages endpoints pass. The proxy forwards POST /v1/messages and its token-count endpoint. It refuses every other request a client makes to the provider. Our tests ran Claude Code non-interactively, with its non-essential traffic turned off. How a default interactive session behaves behind the proxy is part of what a pilot validates.
An error fails closed. The proxy puts a rejection in place before it parses anything, and restores the provider's response only after every check passes. An unexpected error therefore cannot let a tool call through. We tested that by forcing one.
Observe mode works here too. With SHIELD_GATEWAY_MODE=observe on the gateway and Shield in audit-only mode, the proxy releases responses and records them, as described in observe mode. Released responses carry an observed marker instead of approved. On this path, observe mode is covered by unit tests of the proxy, not by the network lab.
What the VPN and proxy need
The guarantees on this page rest on the properties below. Each one says how the reference deployment provides it, and where it falls short: keeping the VPN connected is left to device management and is not tested on macOS, and in the daily-use design a passed-through website is decided on its TLS hostname alone. Keeping the routed provider addresses current is pilot work (see limits). When you evaluate your own VPN or proxy, check each one.
The VPN
-
It sends the provider's addresses, IPv4 and IPv6, to the gateway. In the daily-use design it sends only those; in the full tunnel, everything. Provider traffic that takes another path is not inspected.
Reference: WireGuard routes. For the daily-use design they list Anthropic's addresses.
-
It stays connected, and fails closed. If a developer can disconnect it, or the client sends provider traffic direct when the gateway is unreachable, agent traffic leaves the gateway's path. Keeping it connected is a device-management control.
Reference: WireGuard's routes do not depend on the gateway being reachable, so with the gateway down, provider traffic has no direct path (tested on Linux). Keeping the tunnel up on macOS is not tested.
-
It binds each device to the source address the proxy sees. Each record's user and device rest on this, so nothing may translate the address on the way to the proxy. A VPN that hands out addresses per session must also give the gateway its live record of which device holds which address. Without that, the gateway can still enforce the policy, but its records cannot name the user.
Reference: WireGuard binds each device key to one tunnel address. The gateway refuses an address it has no enrollment for.
The gateway host
-
Provider traffic leaves only through the proxy, and in the full tunnel nothing else leaves. Other ports, UDP 443 (the QUIC port) and IPv6 to the provider are refused or steered into the proxy. Otherwise an agent can take a path the proxy never sees. In the full tunnel, every other destination is refused too: that is what stops an agent pointed at a relay outside the provider's addresses. A full-tunnel gateway that forwards other traffic to the internet loses that.
Reference: an iptables firewall drops forwarding by default, redirects port 443 to the provider into the proxy, and rejects the rest.
The inspection proxy
-
It decrypts with a company certificate that only agents trust. Each agent needs a way to load it. Claude Code reads it from
NODE_EXTRA_CA_CERTS. -
It holds the whole response before releasing any of it. A proxy that streams responses through delivers a tool call before Shield has answered.
Reference: mitmproxy can stream responses; the reference turns that off explicitly.
-
A failure in its Shield integration withholds the response.
Reference: mitmproxy logs an add-on's error and continues delivering. So the reference add-on puts a rejection in place before it parses anything, and restores the provider's response only after every check passes. A forced error is tested.
-
It decides on the TLS hostname and the HTTP host together. It forwards only the provider's Messages endpoints on the API hostname, and refuses other hostnames and bare IPs. In the daily-use design it also passes named websites through undecrypted.
Reference: a Shield add-on for mitmproxy, with mitmproxy's list of passed-through hostnames. A passed-through website is decided on its TLS hostname alone. So in the daily-use design, a client that sends a website's hostname and asks for the API passes uninspected: a known gap, pinned by a check (see browsing).
Another proxy fits if it has these properties and can call the Shield server for each tool call. That includes one your company already runs for TLS inspection. Its Shield integration is an adapter, which a pilot builds and tests; the mitmproxy add-on is the only one built so far. An agent other than Claude Code needs the additions in which agents it covers, including a way to route its provider's traffic.
Who each record names
Every decision record carries:
| Field | Comes from |
|---|---|
| User and display name | The enrollment registry, looked up by the tunnel address that the device's VPN credential is bound to, never by a header or name the client sends |
| Device and tenant | The same enrollment entry |
| Device credential | In the reference deployment, the public half of the device's WireGuard key; the private key stays on the Mac |
| Request and tool-call ids | The gateway's own request id, and the provider's id for each tool call |
| Agent | Inferred from the request's User-Agent, and labelled as inferred. A network gateway cannot verify which program sent a request. |
A VPN credential identifies an enrolled device, not who is at the keyboard of a shared machine.
What we tested (reference deployment)
These are the full-tunnel labs, built with iptables and mitmproxy. The identity lab (24 checks) runs over WireGuard. The routing and inspection lab (25 checks) carries the laptop's traffic in a lab-only GRE tunnel, standing in for the VPN. The daily-use design's checks, over WireGuard, are below. All on 14 October 2026, against Shield main plus pilot branch PR #4354. "Real Claude Code" means the CLI ran end to end, as an unprivileged user. Results are judged by files on disk and what reached the provider, not by logs.
| Case | Result |
|---|---|
| Default provider URL, approved command | Real Claude Code runs it |
NO_PROXY=* and no proxy settings, blocked command | Still blocked: the rule is named, nothing runs |
| Agent pointed at a different approved provider URL | Still inspected and blocked |
| Agent pointed at an unapproved relay | Connection refused; nothing runs |
| Unapproved hostname on an approved address, literal IP, other port, UDP 443, IPv6 | No tool call delivered |
| Inspection certificate not trusted | TLS fails; no fallback to a direct connection |
| Two tool calls in one response, one blocked | Neither runs |
| Claude Code's own Read tool, approved and blocked | Released, or never read |
| Shield stopped, or the whole gateway stopped | Nothing runs; no direct path to the provider |
| Unexpected error inside the proxy | HTTP 400; no tool call |
| An unprivileged user on the client (a Linux stand-in for the Mac) tries to change its route | Not permitted |
| Two enrolled users | Each action recorded with the right user, device, request and tool-call id |
| Forged identity headers, or a forged tunnel source address | Cannot claim another user's identity |
| Unknown key, or a revoked one | No access; the other user keeps working |
| Shield outage | The rejection is recorded with the original user |
That is 49 checks across two lab setups: 25 for routing and inspection, 24 for identity. Another 28 unit tests cover the proxy's release rule in both modes and its destination rule. They include the cases the lab does not reach: degraded, audit-only and approval verdicts, a provider-run tool, an undeclared tool, a truncated or oversized response, and the non-streamed path.
What changes for browsing
There are two routing designs, and both are tested. The choice decides what the network alone can guarantee.
| Daily use (split tunnel, recommended) | Restricted pilot (full tunnel) | |
|---|---|---|
| Sent to the gateway | Only Anthropic's addresses (IPv4 and IPv6) | All of the laptop's traffic |
| Ordinary browsing | Goes direct, with the site's own certificate. Shield never decrypts, logs or slows it, and a gateway outage does not affect it | Unavailable while connected, including Git hosting, package downloads and network calls from MCP servers |
| Anthropic's own websites | Named websites pass through the gateway undecrypted: they share the API's address (see below). Any other hostname on that address is refused, including platform.claude.com, where the Console and docs now live, because it also serves the API | Decrypted at the gateway, then refused |
| Agent pointed at a relay outside Anthropic's addresses (for example Amazon Bedrock or Google Vertex AI, one setting away) | Goes direct, uninspected; the AgentShield hook on the laptop still sees its actions | Refused at the gateway |
| Tested | 26 checks | 49 checks (above) |
Why Anthropic's websites ride the tunnel. On 2026-10-14, api.anthropic.com, claude.ai, console.anthropic.com, docs.anthropic.com and www.anthropic.com all resolved to the same address, inside a block registered to Anthropic. Routing the API's address therefore routes those sites too, and no unrelated site. The gateway passes a named list of Anthropic websites through untouched, by their TLS hostname. Shield keeps no record of them, but the gateway host still sees their connection metadata (address, hostname, volume). If the gateway is down, those websites are unreachable until it is back.
Any other hostname, or a bare IP, on that address is refused, because it could be another way to reach the API. An Anthropic website missing from the list therefore fails in a browser until it is added. Install the inspection certificate for agents only (Claude Code reads NODE_EXTRA_CA_CERTS), not system-wide: a browser that trusted it would let the gateway decrypt such a site before refusing it.
The design assumes Anthropic serves its API on none of the passed-through hostnames. On 15 October 2026, an unauthenticated API request got these replies:
| Hostname | Reply |
|---|---|
platform.claude.com | The API's own reply, as from api.anthropic.com |
console.anthropic.com, docs.anthropic.com | A redirect to platform.claude.com, where the Console and docs now live |
www.anthropic.com | 404 |
claude.ai | A browser challenge |
So platform.claude.com is never passed through. It is refused like any other hostname, and the Console and docs do not load in a browser while connected. A pilot re-checks the list against the live service.
What we tested in the daily-use design, with real Claude Code and Linux containers standing in for the Mac and the gateway host:
| Case | Result |
|---|---|
| Ordinary HTTPS over IPv4 and IPv6, HTTP, UDP on port 443 (the QUIC port) | Direct, with the site's own certificate. The gateway's counters and both audit logs are unchanged |
| Claude Code on its default URL, and on a changed approved URL | Inspected and attributed to the enrolled user; a blocked call never runs |
| An Anthropic website on the API's address | Passed through without decryption: the site's own certificate, and no Shield or gateway record |
| Another hostname, or a bare-IP URL, on that address | Refused by the gateway as an unapproved destination; Claude Code pointed at another hostname runs nothing |
| IPv6 to the API | Steered into the tunnel and refused there. Claude Code tried IPv6 first, then fell back to IPv4, where it was inspected |
| Shield down, or the gateway down | The agent gets no response it can act on, and has no direct fallback; browsing keeps working |
| Unregistered VPN key | No access to the API; browsing works (revocation is tested in the full-tunnel identity lab above) |
| VPN disconnected, or the agent pointed at a relay outside Anthropic's addresses | Leaves on the direct path, uninspected. |
| A client that sends a website's hostname in TLS but asks for the API | Passes through uninspected. Only decrypting every website closes this. The full tunnel does: its proxy refuses a request whose TLS and HTTP hostnames differ (unit-tested) |
The last two rows are the design's three known gaps. The lab pins each one with a check, so closing a gap has to be deliberate.
Run both. In the daily-use design the network inspects the agent lane while it stays in the lane. The AgentShield hook on the laptop sees the actions of an agent that leaves it.
Limits to plan for
- The VPN must stay on. Someone who controls their own Mac can disconnect a VPN, and in either design agent traffic then goes direct, uninspected. Keeping the tunnel up is a device-management control you configure. We have not tested it on macOS.
- Daily use: routed addresses must stay current, and drift fails open. The daily-use design routes Anthropic's published address ranges. If the provider starts using an address you don't route, agent traffic to it goes direct, uninspected, with no alert. The lab used static addresses; keeping the ranges current, and monitoring them, is pilot work.
- Agents must trust the inspection certificate. Claude Code does, through
NODE_EXTRA_CA_CERTS; check how another agent's runtime loads an extra certificate. A client that pins its certificates will fail rather than be inspected. - Developers keep their own provider credentials. The gateway passes them through unchanged. The pilot tested API keys; subscription sign-in has not been tested. Claude Code 2.1.296 signs in and refreshes its token at
platform.claude.com, which the reference proxy refuses, so expect sign-in to fail while connected until a pilot designs for it. - Enrollment is an administrator step in the reference deployment. Self-service enrollment with your company sign-in, and a prompt that tells an unenrolled developer how to enroll, are in design.
- Full tunnel: provider addresses are fixed in the lab. The lab used static provider addresses. Keeping the firewall's approved addresses current as the provider's DNS changes, while failing closed rather than open, is pilot work.
- Records stay on the gateway. Decisions are in Shield's hash-chained audit log and the gateway's delivery log, written best-effort. Neither is sent to AI Agent Lens Cloud in the pilot.