How we tunnel a public URL into an untrusted sandbox
Have you ever thought about how a sandbox tunnels a public URL out to the world when one end of the tunnel is untrusted code owned by the user? Take an example: a localhost:3000 app running in a sandbox inside a VM. The user wants a public link for it. It looks like a reverse proxy. It isn't, because the app behind the link is untrusted code. It feels easy, but for security it's full of hiccups. The naive version, and the holes it leaves. If there's just a public URL, anyone can hit it, and if the user doesn't want it shown to the world, that's not acceptable. If we put a secret token in the URL, it leaks, via browser history, referer headers, and server logs. And either way, the hostile app can steal or forge the visitor's session. A normal reverse proxy forwards traffic to a backend it trusts: your own app. This gateway forwards to a backend it must distrust: the user's code running in the VM. It's still a reverse proxy, it's just one whose backend you can't trust. That single inversion is why every byte crossing it, in both directions, gets inspected. What we actually want: a secure runtime URL, behind a reverse proxy, that can't be exploited, not against the visitor and not against the worker the sandbox stays executable and runnable while the URL is live the user can share it with anyone they choose, but it is not public This is deliberately not a plain browser-link architecture. It works through cookies, and it needs one extra sign-off step before the user gets a usable session. So how do we stop a random person from just landing on a particular sandbox's exposed URL? HMAC. How it actually works, in real time. There is no dumb reverse proxy blindly routing traffic into the sandbox. First, the traffic hits the edge (Caddy, wildcard TLS) and lands on cmd/preview-gateway. Its first job is to parse the host into sandboxID + port. Both are client-supplied through the URL, so the sandboxID is then strictly UUID-validated. Second, it verifies the token against its signature (HMAC). If it verifies, then on the very first request it does the handoff: it moves the token into a __Host- cookie and 303-redirects to strip the token out of the URL. This matters, because next time the user doesn't have to send the whole token URL, the token now lives in the cookie, so a clean URL is enough. From then on, the gateway checks whether the credential comes in the cookie or still in the URL, and re-verifies it every time. The cookie value is the whole token, nothing is stored server side, and verification is just recomputing the HMAC, so there's no DB lookup and it scales. Then, to learn which worker the sandbox is on, the gateway asks the orchestrator, which returns the worker id and endpoint. Having that, we route the request to that worker. We deliberately don't do this lookup from the gateway directly, because we don't want the client-facing edge to hold database access, only the orchestrator gets that job. Now, before the request reaches the worker, the gateway does its custodial work on the proxy hop: it strips the visitor's cookies, so the untrusted app never sees your session, and injects the worker-token instead, so the worker plane will authorize the request. Without that token the worker returns a not-authorized error. Once the worker has authorized it, the worker deletes that worker-token header, because the request is about to enter the user's sandbox and we don't want any of our tokens left inside it. Then, for example on port 3000, the worker's Firecracker process dials into the slot's network namespace, reaching 172.16.0.2 , the sandbox namespace, which is reachable only inside that slot's private network namespace. That's the app. The whole trick: the gateway is a membrane, not a proxy. The untrusted app can't be reached without a signed key, can't see the key, can't steal your session, and can't forge one. The sandbox is untrusted by construction, so every control lives one level outside it.