Internals
Requests cross into a child process as one message, and the result comes back as one. Between those two the pool picks a worker, the sandbox builds a fresh V8 isolate, and the program runs. Each chapter here is written from the code it describes and names the file that owns a behaviour.
Layers from host to isolate
Section titled “Layers from host to isolate”flowchart TB host["your host<br/><small>declares tools, decides bounds</small>"] adapter["adapter<br/><small>@supolka/cloudflare-codemode-executor</small>"] executor["IsolateExecutor<br/><small>the native contract, in process</small>"] pool["worker pool<br/><small>supervises children, queues, retires</small>"] child["child process<br/><small>one execution at a time by default</small>"] sandbox["sandbox<br/><small>builds the isolate, holds the bounds</small>"] runtime["runtime<br/><small>the standard library, from the specifications</small>"] isolate["V8 isolate<br/><small>empty global, fresh per execution</small>"] host --> adapter --> executor host --> executor executor --> pool --> child --> sandbox --> isolate runtime -. installed into .-> isolate
Both sides of every boundary implement one contract, a package that declares no dependency. It defines the wire types and the codec, and it declares every failure the executor reports as a class that composes its own text. But it names no runtime. Hosts on any JavaScript engine can implement against it, and the executor protocol specifies it.
One request becomes one isolate. Inside the child, the sandbox restores a standard library from a startup snapshot and installs the capabilities the request granted. It compiles an entry module around the program, races that module against the run clock, and disposes what it built when the run ends. Above it the pool owns the child processes, picks the child a request runs in, and replaces one that crashes or wears out. execute rejects the call with a typed fault when the queue is full, so the queue never grows past its bound.
Remote callers meet a socket in front of the pool: the server. Their own functions cross it with the request, so a program running on another machine still calls tools that run in the caller’s process.
Only the adapter speaks a foreign dialect. It implements the @cloudflare/codemode shape over the native contract. The same package ships the client that connects to a daemon.
Boundaries at the isolate and the process
Section titled “Boundaries at the isolate and the process”flowchart LR
subgraph parent["host process"]
caller[caller code]
end
subgraph worker["child process"]
direction TB
supervisorside[sandbox]
subgraph iso["V8 isolate"]
guest[the program]
end
end
caller -- "request, one message" --> supervisorside
supervisorside -- "result, logs" --> caller
guest -- "tool call: three strings" --> supervisorside
supervisorside -- "envelope: one string" --> guest
supervisorside -- "tool call" --> caller
caller -- "answer" --> supervisorside
Two bounds apply at the isolate boundary: the memory limit and the interrupt. Each allocation the program makes comes out of the heap of one V8 isolate, and isolated-vm creates that isolate under the request’s limit. Deadlines are measured outside the isolate, so a call still settles even when the program cannot be interrupted.
Behind the isolate, the process boundary bounds the cost of a native crash: one worker, one caller, a typed fault the caller may retry, and a replacement child. That boundary is also a ceiling on the size of one message. So every message a program sends outward is measured where it is built, not where it is sent. Where a deployment has a network boundary, the same contract crosses it, and the caller’s own functions travel with the request. The threat model states what each boundary enforces and what it does not. Every run of pnpm verify measures those claims, under the method of the benchmark.
Reading order
Section titled “Reading order”Questions about one program go to the sandbox, questions about load to the pool, and questions about what a program can call to the runtime. But the server and the adapter begin to matter once the executor sits behind a socket, or behind somebody else’s dialect.