cd /news/ai-agents/ai-sandbox-networking-compared-tenso… · home topics ai-agents article
[ARTICLE · art-128806] src=pub.towardsai.net ↗ pub= topic=ai-agents verified=true sentiment=· neutral

AI Sandbox Networking Compared: Tensorlake vs E2B vs Daytona vs Fly.io

Tensorlake redesigned its sandbox proxy to collapse a second Layer 7 hop down to Layer 4, publishing before-and-after numbers for the change, according to a comparison of sandbox networking across Tensorlake, E2B, Daytona, and Fly.io. The gateway-to-forwarder hop still authenticates with mTLS, and the L4 forwarder reads a short authenticated routing preamble once per connection before splicing bytes through. Because the L4 forwarder can no longer see individual requests, Tensorlake derives sandbox activity from byte flow, tracking bytes moved per tunnel and periodically reporting the count to the dataplane to reset the sandbox's idle timer.

by read13 min views1 publishedSep 14, 2026

Every sandbox platform for AI agents runs into the same problem on day one. A request from the open internet has to reach an isolated microVM or container, and something has to decide where that request goes before either side trusts the other.

That “something” doesn’t have to be a single hop.

A sandbox ingress path can run through several proxies in sequence, an edge gateway, then something deeper inside the platform’s own network, before a request ever reaches the sandbox.

Each hop makes its own choice about how much of the traffic it actually looks at.

A hop can operate at Layer 4, moving TCP or TLS bytes around without looking at what’s inside them.

Or it can operate at Layer 7, parsing every HTTP request and making a decision based on what it finds.

Tensorlake is a good example of why that’s a per-hop choice, not a whole-path one: it keeps its outer edge gateway at L7, and the change this piece spends the most time on is a second hop, further into its dataplane, that moved from L7 down to L4.

I spent a few days reading how four platforms document this hop. I went in expecting four minor variations on the same idea.

I came out with something more interesting: three platforms that document the mechanism well and one that documents the mechanism and shows its work on why it’s built that way.

That gap is what this piece is about.

One quick note before diving in: Modal sits this comparison out, its inbound-access model is well documented and secure by default, but the actual routing mechanism behind a public URL isn’t documented at the depth the other four platforms here document theirs.

Here’s the tradeoff, stated once, before any vendor gets involved.

An L7 proxy parses every request. It can read headers, make a fresh routing call, rewrite something, check a token, all per request. That flexibility isn’t free. The proxy has to understand the protocol it’s forwarding, and it pays a CPU cost for that understanding on every single request that crosses it.

An L4 proxy skips the understanding entirely.

Once a connection is set up and the proxy knows where it’s headed, it can move bytes at close to zero per-byte overhead, often using kernel primitives like splice() that never copy the payload into userspace at all. The catch: it can't see inside the connection anymore to make a new call. Whatever logic you needed has to get decided once, up front, not per request.

So which one wins? Depends entirely on what’s crossing the hop.

That second row is where an L4 hop earns its keep. Keep that in mind, because it’s the whole reason Tensorlake’s numbers below look the way they do.

This is the one platform in this piece that changed its own architecture and published before-and-after numbers for the change. Here’s the mechanism first, numbers after.

Tensorlake’s sandbox proxy used to run two Layer 7 hops back-to-back:

That second hop being in-process with the orchestrator wasn’t a small detail. It meant a routine orchestrator deploy could interrupt a live connection passing through the proxy, because the two things weren’t separable. That’s the kind of coupling you don’t notice until a deploy lands mid-transfer.

The redesign collapsed the second hop from L7 down to L4:

The gateway-to-forwarder hop still authenticates with mTLS (mutual TLS). The forwarder reads a short authenticated routing preamble once, at the start of a connection, before it starts splicing bytes through.

There’s a second problem an L4 hop creates that’s easy to miss: the dataplane still needs to know when a sandbox is still active, so it can extend the idle timeout.

An L7 proxy got that for free, it saw every request go by. The L4 forwarder can’t see requests anymore, so Tensorlake derives activity from byte flow instead: the forwarder tracks bytes moved per tunnel and periodically reports that count to the dataplane, which uses it to reset the sandbox’s idle timer. Same signal, different source.

The forwarder also runs kernel TLS. This is a narrower change than it sounds. It moves only the TLS record layer into the kernel, the payload is still encrypted and decrypted exactly as before. What’s different is that it’s no longer staged and parsed in userspace to get there, which is what makes splice() usable on the encrypted path.

And here’s a detail I appreciate more than I probably should: there’s no fallback. The daemon refuses to start if the kernel can’t attach the TLS ULP (upper-layer protocol), full stop. It needs Linux 5.1 or newer, TLS 1.3, and a pure software stack.

No quiet degradation, no “best effort.” If your kernel can’t do it, the thing doesn’t boot. That’s a real design decision, and it’s the kind a lot of teams fudge.

Now, the numbers. Worth being precise about the test conditions before looking at them: one connection, one direction, over loopback, on a single desktop-class machine, moving 4 GB per run. That’s a controlled measurement of the proxy path itself, not a real network path with real latency and jitter sitting in front of it.

Removing the redundant L7 hop did most of that work by itself, on both throughput and CPU. Kernel TLS and splice added roughly another 20% of throughput on top. Two separate wins, worth keeping separate in your head. Of the forwarder’s own CPU budget, cryptographic operations account for about a quarter of it, and I want to be precise about that: a quarter of the forwarder’s CPU, not a quarter of the system.

One more thing worth saying plainly, because it’s easy to want it to be true: none of this measures end-to-end latency for an actual agent’s HTTP call. It measures bulk-transfer throughput and forwarder CPU, one connection at a time. A short API call is bound by handshake and round-trip time, not payload size, so this fix doesn’t directly touch that traffic shape. It’s the right fix for what it targets, and it doesn’t pretend to be more than that.

E2B puts a component called Client Proxy at the edge, separate from the control-plane API.

It’s an L7 router in the plain sense: it parses the host header, looks up which node the sandbox is currently running on in a Redis routing catalog, and reverse-proxies the request to that node. That’s not the last hop, though. The node itself runs a second proxy, inside the orchestrator process, that takes the request the rest of the way into the VM.

You can see the routing scheme in the URL itself. A sandbox exposing port 3000 gets a public address like 3000-<sandbox-id>.e2b.app, the port is literally the first label in the hostname. Inside the VM, a daemon called envd handles process and filesystem operations over Connect RPC, on port 49983 by default.

The part I found genuinely interesting is the auth setup, which splits into three separate, independently-documented mechanisms. I’m calling this a “three-tier” model as a description, not because E2B uses that term anywhere.

What stands out is that these three don’t depend on each other. You can lock down the control plane and envd tightly and still leave a sandbox’s public HTTP port wide open, because the traffic token is a deliberate, separate opt-in sitting on top. More granular than a single switch, which is good. But it also means the out-of-the-box default for a public sandbox URL is unauthenticated, until you flip that third switch yourself.

Daytona’s proxy is described plainly in its docs: a dedicated HTTP proxy, host-based routing, at {port}-{sandboxId}.{proxy-domain}. It resolves which runner is currently hosting a sandbox, injects the auth headers it needs, and forwards. Nothing exotic here. It’s a straightforward mechanism, clearly documented, which is exactly what you want from an ingress hop you didn’t build yourself.

Two preview URL types, two auth flows. A standard preview URL keeps the sandbox ID visible and needs a separate token in the x-daytona-preview-token header, which resets whenever the sandbox restarts. A signed preview URL embeds the token in the URL itself, no header required, at the cost of a short default expiry (60 seconds, though you can configure that), because anyone holding the URL is holding the credential. Three ports are reserved and always require auth no matter what: 22222 for the web terminal, 2280 for the toolbox interface, 33333 for the recording dashboard.

If the built-in proxy isn’t enough, you can front it with your own. Your proxy sits in front of Daytona’s routing, forwards X-Forwarded-Host so the sandboxed app still sees the right original host, and you own auth, error pages, and branding yourself. Daytona still does the actual sandbox-side proxying underneath, you're adding policy, not replacing the mechanism. Isolation is where Daytona makes you pick a side, on purpose. The default sandbox class is a Linux container, and Daytona’s own security documentation names the runtime specifically: Sysbox, which it describes as providing VM-level isolation without hardware virtualization overhead. Linux user-namespaces map the sandbox’s root user to a fully unprivileged user on the host, with per-sandbox UID/GID mappings. A full hardware-virtualized VM is available too, Linux or Windows, but it’s a separate sandbox class you have to explicitly select, and it can only be created from a VM snapshot, not the declarative image builder containers use.

That VM boundary is also what the docs tie , resume, fork, and hot snapshots to specifically, calling those VM-only capabilities. You don’t get them for free with the container class.

This one surprised me a little, in a good way. Sprites don’t get their own ingress system. They just plug into whatever Fly.io already runs for every app on the platform.

Fly announces its IP blocks from every datacenter over BGP (Border Gateway Protocol). That’s Anycast: a request lands on whichever edge is topologically closest, automatically, no region picker, no separate load balancer to configure. From there, Fly Proxy terminates TLS for public apps, tags the request, and sends it over a WireGuard tunnel to the nearest worker running the target machine. That worker’s own Fly Proxy hands it off locally, and the response comes back the same way.

All of the routing state behind that, which machines exist, where, how healthy, comes from Corrosion. It’s Fly’s own gossip-based catalog: a SQLite database that propagates across the fleet via gossip, so most reads are served locally from eventually-consistent state instead of round-tripping to a central database somewhere.

Sprites use exactly this stack. Request a public URL for a Sprite, and that generates a Corrosion update that spreads across the fleet. The Sprite becomes reachable over HTTPS through the same edges, the same WireGuard backhaul, the same gossip propagation as every other Fly app.

There’s no Sprite-specific ingress optimization documented anywhere, and honestly, that’s the actual design decision here. Fly didn’t build sandboxes a special path. It decided a Sprite is just another workload on infrastructure that already had to solve global routing at scale.

E2B and Daytona sit close together at the routing layer. Both run a dedicated L7 proxy that reads the host header, looks up sandbox location somewhere, and forwards. The lookup mechanisms differ, a Redis routing catalog for E2B, an internal runner-resolution step for Daytona, but the shape is the same host-based L7 pattern either way.

Fly Sprites are on a completely different axis. The mechanism is Anycast plus WireGuard plus gossip-propagated state: general infrastructure that happens to carry Sprites, not something purpose-built for sandboxes.

And Tensorlake is the one that moved a hop from L7 to L4, and can show you, in numbers, what that move bought.

The asymmetry that matters here isn’t about which layer anyone picked.

Only Tensorlake publishes quantified, before-and-after rationale for its ingress design, at least in what each platform makes public. E2B, Daytona, and Fly.io all document their mechanisms clearly, clearly enough that I could write the sections above straight from primary docs without guessing at anything.

But none of the three publish the benchmark numbers behind why that mechanism won out over something else. That’s a different gap than Modal’s, where the mechanism itself isn’t documented in comparable depth at all. Here, the mechanism is documented. The reasoning for choosing it isn’t, except at Tensorlake.

Tensorlake’s numbers are useful specifically because they don’t collapse two different changes into one. Removing the redundant L7 hop, by itself, accounted for most of the throughput gain and nearly all the CPU savings.

Kernel TLS and splice, layered on top, added roughly another 20%. Two mechanisms, two separate wins. Treat them as one and you’d overstate what either did alone.

What the numbers can’t show you is what you give up.

An L7 proxy can rewrite a header, enforce a path rule, make a fresh call, on every request, inside one connection. An L4 forwarder, once it’s spliced a connection, isn’t looking inside it again. Tensorlake’s answer is the authenticated preamble, pushing that decision to once per connection instead of once per request.

That’s a good trade for the traffic this hop carries. It would be a bad trade for a hop that genuinely needs per-request logic, and it’s worth being honest that this isn’t a universal upgrade, it’s a fit for a specific shape of traffic.

Scope matters as much as the numbers do here. This is bulk-transfer throughput and forwarder CPU, not end-to-end latency for a typical tool call. Short, handshake-bound requests don’t move enough bytes for a per-byte win to matter much either way. If your own ingress hop looks like that, an L4 rewrite modeled on this one won’t get you what it got Tensorlake, because you’re not paying the cost it was built to remove.

None of this settles whether L4 or L7 is correct for a given hop, because that depends on the traffic crossing it, not on which platform’s docs you read last.

What it does settle: how much of each platform’s own reasoning you can check before you build on top of it.

E2B, Daytona, and Fly.io each document their routing mechanism well enough to compare, and I mean that, this isn’t a backhanded compliment. Fly’s answer is arguably the most interesting of the three precisely because it isn’t sandbox-specific at all. Modal’s isolation story is solid, its ingress internals just aren’t public enough to sit in this comparison.

Tensorlake is the only one of the four that shows the before-and-after math for its own design decision. Not a claim to take on faith. A number you can go check yourself against a specific test machine, a specific connection count, a specific date.

That’s the actual difference in this comparison. If I were picking a platform for a workload where this hop matters, that’s the one fact I’d weight most, not because the others are worse engineered, but because I could verify this one without having to run the benchmark myself first.

AI Sandbox Networking Compared: Tensorlake vs E2B vs Daytona vs Fly.io was originally published in Towards AI on Medium, where people are continuing the conversation by highlighting and responding to this story.

── more in #ai-agents 4 stories · sorted by recency
── more on @tensorlake 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/ai-sandbox-networkin…] indexed:0 read:13min 2026-09-14 ·