Skip to main content
Every handler Utter runs is code a language model generated from a sentence. It is untrusted by construction, so it runs only inside a real isolation boundary: gVisor. Plain Docker is not that boundary. The runtime refuses to run an untrusted bundle without gVisor rather than silently fall back to something weaker. On top of the kernel boundary, a hardened run-spec strips the container down to nothing it does not need, and a trusted sidecar in front keeps the untrusted handler away from the money path entirely.

gVisor, not plain Docker

The handler runs under gVisor (runsc), a user-space kernel that intercepts syscalls, on an operator-provisioned host. Plain Docker (runc) shares the host kernel and is not a security boundary for untrusted code.
If gVisor is not available, the runtime refuses to run an untrusted bundle. It does not degrade to runc. A missing isolation boundary is a hard failure, not a warning, because running generated code on the host kernel is exactly the thing the sandbox exists to prevent.

The hardened run-spec

Both backends enforce the same invariants on every untrusted container. None of them is optional.
Never privileged. Never host networking. No added capabilities, capDrop ALL, and no-new-privileges. The container cannot escalate.
The platform environment is empty. No secrets, no keys. The only thing injected is a short-lived, scoped data-proxy token, and only per request. See the egress proxy.
Network none. The container has no egress path except the data proxy, which is attached host-side. It cannot open a socket to the internet on its own.
Read-only root filesystem. tmpfs mounted noexec and nosuid, so the handler cannot drop an executable and run it, or gain privilege through a setuid binary.
Positive pids, memory, and cpu limits, plus a runner-enforced timeout and an optional disk quota. Live example caps: 256 pids, 256 MiB memory, 0.5 cpu. A handler cannot fork-bomb, exhaust memory, or run forever.

Size caps

Request and response bodies are each capped at 1 MiB. An oversize request is rejected before the handler is ever invoked, so a large payload cannot be used to run compute the buyer did not pay for. An oversize response fails closed with a 502.

Two containers per endpoint

A deployed endpoint is not one process. It is two, with a trust boundary between them.
1

The untrusted handler

Runs the generated code. It has no facilitator URL and no token, and it can talk only on its per-slug internal network. It cannot reach the money path even if it wanted to.
2

The trusted sidecar / gate

Sits in front. It owns the escrow response gate: it reads the 402, verifies the payment, invokes the handler, classifies the response, and settles. It holds the facilitator token.
3

Traefik routes the public host

Traefik routes the public host to the sidecar, never directly to the handler. Every call reaches the money path first.
Splitting the endpoint in two means the untrusted code never holds a credential that could move money, and never sees the facilitator. Even a fully compromised handler can only return a response, which the sidecar will then classify and, if it is bad, refuse to charge for.

The static gate and egress proxy

What runs before the build and how the handler reaches an upstream.

The escrow response gate

The money path the trusted sidecar owns.

Provisioning

Standing up the gVisor host and the wildcard TLS domain.

The deploy pipeline

Where the sandbox sits in build and deploy.