- Access exclusive content
- Connect with peers
- Share your expertise
- Find support resources
An internal support agent with access to customer account records sits on a private subnet with no public DNS. Our AI red teaming platform cannot send it a single adversarial prompt. Neither can an attacker who is outside the network, which is why it was deployed there.
Production is also the wrong place to test. Finding a jailbreak in a system real users are already talking to means finding it late; the value is in catching it in dev, staging, or QA, which no one exposes to the internet. The environments where red teaming belongs are, by construction, the hardest to reach from outside.
We built Network Channels so those systems could be red teamed without the customer opening an inbound port or adding us to a firewall allowlist.
Automated AI red teaming means sending adversarial input to a system and evaluating what comes back, which requires access to their secure environments over their network. Until we built this, access would only be possible for a public endpoint we could call, and that covered the first wave of production AI.
Then AI moved inward, into internal agents and assistants with real system access, and security testing moved earlier, into the pre-release gate. Both shifts point at environments with no route from the internet, and the public-endpoint assumption failed in three ways:
Open an inbound port, optionally allowlisted to our egress IPs: It puts an internet-reachable entry point into an internal network. Allowlisting narrows who can reach it without removing the listener, and puts our IP churn into the customer's firewall change process: security review, change window, sign-off, and an exception to re-justify at every audit. We shipped it as a secondary option, and weeks can pass before the first scan runs.
VPN or site-to-site link: Routing is bidirectional, so the link exposes our network as much as the customer's. Every tenant's link also lands in that same network, which turns one compromised customer into a route toward the rest.
Run the scanner on-premises: Places our attack library and engine inside customer infrastructure which adds overhead of supporting it in every customer environment, so a new attack ships at the pace of the slowest upgrade.
The idea that came to us is: If the customer cannot let us in, the customer can come to us. A process inside their network opens a single outbound WebSocket to our infrastructure and holds it open. Requests travel down it, responses back up. Only the direction is reversed, so the connection crossing the trust boundary was initiated from inside. It needs no listener, no allowlist entry, and no perimeter change.
Figure 1. The engine and server run in our infrastructure, the client in the customer's. Only the client's outbound connection crosses the boundary.
Stack: Client and server are both Golang 1.25. Goroutines (Go's lightweight threads) make thousands of long-lived connections cheap, and the client ships as one static binary. The server adds Echo, gorilla/websocket, GORM (Go Object Relational Mapper) over Postgres, plus Prometheus and zerolog.
Client: Runs in the customer's environment. It dials out, executes proxied requests against internal endpoints, returns responses, and reconnects on its own. It also surfaces the connection state in the UI, because a tunnel that has silently stopped forwarding is worse than one visibly down.
Server: Terminates client WebSockets, holds per-tenant routing state, and accepts proxy calls from the red teaming engine.
Channel: The addressable unit and the only persisted entity. Setup is a service account, a role, and a client pointed at the channel ID.
Figure 2. One request end to end, over a tunnel that was already open before any attack traffic existed.
The client opens the tunnel first, exchanging the customer's service account credentials for a short-lived token. The server validates the channel and holds the connection open.
On each proxy call the server picks one of the channel's connected clients round-robin, generates a fresh routing ID, and registers a Go channel for the reply before writing the request onto the tunnel. When the response returns, the server matches the routing ID and checks that it arrived on the channel the request was dispatched to. Because the tunnel is already open, the per-request cost is two hops over an existing connection.
The tunnel carries REST, SSE (Server-Sent Events), and WebSocket targets, so a target's transport does not determine whether it can be scanned.
The server is a multitenant application, so tenants have to stay isolated and no one tenant can degrade the rest. Four layers:
Server and token URLs are intentionally compiled into the client binary rather than reading from environment configuration, which prevents a tampered deployment being pointed elsewhere.
Round-robin dispatch, not load-aware: Every client on a channel can reach every target, so treating them as interchangeable gives us high availability out of the box. The cost is that the server cannot see how loaded a client is. Response times vary widely, so a client can pick up its next request while several slow ones are still outstanding.
Reject old clients rather than try them: Customers upgrade on their own schedule, so the fleet always runs a spread of versions. Each request mode declares a minimum version, and the server checks the oldest client on the channel before dispatching. That gives up best-effort delivery, deliberately: a failure inside an old client surfaces as a target error, sending whoever debugs it to the customer's application.
Channel status is computed live: Status comes from current connection state rather than a database column, so it can never be stale. Listing and filtering channels then happens in application code instead of SQL, and that cost grows with a tenant's channel count.
Enterprises decrypt their own outbound TLS: Egress inspection re-signs TLS with an internal authority the client has never seen, so the connection fails certificate verification. The fix is a custom CA (Certificate Authority) bundle supplied as a chart value and appended to the system roots.
Egress frequently has to traverse a forward proxy: Many networks route all outbound traffic through one, and a client that ignores proxy configuration never connects.
Not every customer runs Kubernetes: The Helm chart fits most of the customer environments, but it assumes a cluster. Teams running the client on a single host or on ECS (Elastic Container Service) have had to improvise; reach depends as much on packaging as on protocol support.
In the last 3 months, more than 90% of the private customer targets have connected through our network broker. More than 1.5 million proxy requests have been served through this fleet in just the last 30 days with <400 milliseconds of p95 latency overhead.
| Subject | Likes |
|---|---|
| 5 Likes |

