Architecture
The system runs model-written JavaScript somewhere it cannot hurt anyone, and returns what the code produced. Three questions about that sentence are answered in the threat model: which boundaries hold it up, which ones are out of scope, and which ones belong to whoever runs the thing. Underneath those boundaries sits the machinery: one execution from the caller down to the isolate and back, and the rule deciding what the isolate is allowed to contain.
Path of one execution
Section titled “Path of one execution”A host holds an executor, and the executor protocol specifies the native contract it programs against. That contract lives in a dependency-free package both sides of the wire implement. Hosts speaking the @cloudflare/codemode dialect hold the adapter instead, one facade carrying every convention of that dialect over any native executor. The remote executor speaks the versioned RPC protocol and knows nothing about isolates, processes or the machine on the other end. That ignorance lets it run unmodified on any JavaScript runtime. In packages/executor/src/server, the server accepts that protocol over WebSocket: the listener applies the connection policy, and a per-execution bridge joins the caller’s session to the pool’s session. That bridge owns stub lifetime and the translation between the caller’s dispatch grammar and the sandbox envelope.
Behind the server sits a pool of supervised child processes. It decides which child takes the work, bounds how much work is in flight, and turns overload into an immediate typed fault, not an unbounded backlog. Each child is an ordinary Node process joined to the parent by one IPC channel, and it is expendable by design. A native crash inside a child kills that child and nothing else.
Inside a child, an execution runs in a fresh V8 isolate created through isolated-vm. That isolate starts as a bare context with no capabilities at all. Everything the generated code can reach was installed on purpose: console, timers, fetch, the standard library, the module graph, and the provider proxies that turn a tool call into a message back to the host. When the execution settles, the isolate is disposed, and nothing survives it.
Alongside that path the server answers the operator with a liveness probe, a readiness probe and a metrics scrape. Readiness reports whether this instance serves at all. But a saturated queue is not that fact: saturation reaches the caller as a typed fault and the operator as queue depth. A shutdown turns readiness away first and holds the listener open for a configured delay, because a platform stops routing to an endpoint asynchronously. Through that delay the open connections keep working, and a caller routed here in the meantime is still served. Measurement goes through the OpenTelemetry API alone. That API performs no operations until a host registers an SDK, so this repository never picks a monitoring backend on anyone’s behalf. Whoever composes the process decides where the measurements end up: an OTLP collector, a Prometheus scrape, both or neither.
The standalone daemon in apps/daemon is that composing process, and it serves two surfaces, not one. Its protocol port carries the executor: the WebSocket upgrade and the platform’s probes. A second address, private by default, carries the admin app. That app reports what the daemon is doing, which value of every setting is in force and where that value came from. It also offers a maintenance switch that takes the instance out of rotation without ending the work already on it. Its first client is the command line, so anything an operator can do over HTTP they can do from a terminal, under the same names.
Every setting is declared once, as a flag carrying its own environment variable, and a flag wins over the variable while the variable wins over the default. The daemon rejects at boot any value that would leave it serving nothing, registers the providers that give its measurements somewhere to go, and turns a shutdown signal into a drain bounded by a configured grace. Hosts that would rather embed the executor in their own process use IsolateExecutor directly and get the pool without the network hop. Hosts that want the daemon inside their own process import it as a library, bring their own logger, subscribe to the lifecycle events and keep the process to themselves.
That daemon ships as one container image, built in two stages from a single Debian base. Both stages share that base, so the isolated-vm binary compiled with the toolchain of the first stage meets at runtime the glibc it was compiled against. Only the daemon, its production dependencies and that binary reach the second stage. The image runs as a user without privileges, needs no writable path outside its own memory, and carries its command line under its own name. So an operator asks a running daemon for its status the same way from a terminal and from a container. deploy/ holds the manifests that run it: a Compose file for a laptop, the executor as a workload of its own behind a Service, and the executor beside the calling application in one Pod over the loopback address.
Runtime capability doctrine
Section titled “Runtime capability doctrine”These rules govern the inside of the isolate. They decide what exists there, what it is made of, and what the product is entitled to promise about it. Design carries the rules shaping the modules that implement them.
- Start the sandbox global empty. Every capability arrives as an explicit, tested addition with a place in the allowlist. A runtime that starts full and subtracts is the wrong direction for code nobody reviewed, because subtraction is proven complete only by enumerating a surface nobody has ever fully enumerated.
- Target ECMA-429, the minimum common web API, and follow the standard governing each interface, per decision 0008. Every divergence is a row in Runtime deviations, and compatibility with the dialect stays proven by the differential suite.
- Choose the thinnest material that provides a capability: a reference implementation of the specification where pure JavaScript suffices, a thin in-sandbox facade where the specification is thin, a host bridge only where a real privilege is needed.
- Keep the environment uniform. Capability sources are emitted unconditionally, never gated on what the submitted code appears to use. Code that behaves differently depending on how it was written is not a runtime. It is a guess.
- Write no extensive instructions for the model. Familiar code works because the runtime is standard. But an unfamiliar case explains itself through a pinned just-in-time error naming the replacement.
- Report what the entry reported. A promise rejected with no handler never replaces the outcome of a run and never fails an unrelated call, because the call that happened to be running when it surfaced is not the run.
- Run every execution in a fresh isolate. But across executions only the standard library is cached, serialized once per process into a V8 startup snapshot that every isolate is restored from. Retire a long-lived child process after a bounded number of executions.
- Default concurrency inside a child to one execution, so a native crash kills exactly the culprit. The pool bounds total concurrency and the queue applies backpressure above it.
- Treat the memory limit inside the engine as soft. Its hard backstop is a process-level memory watch plus container limits.
- Treat the protocol and the executor semantics as the product contract, never the sandbox implementation. The runtime is swappable inside one package, and the suite proves the outside unchanged.
Primitives map
Section titled “Primitives map”primitives.yaml next to this page is a machine-readable list of the kinds of thing this system is made of. Each entry names a primitive, places it in a group, gives a one-line summary, and points at the code implementing it. Invariants are listed separately, because an invariant is not owned by a directory: it is a property the whole system holds up.
The map is an inventory of kinds, so it holds still while features move. It answers “what kinds of thing exist here” for a reader who has never opened the tree. But it answers that only while the rules below hold.
- Add an entry when its code enters the tree, never before. A planned primitive is not a primitive.
- Edit an entry when the thing appears, disappears, or changes meaning. Nothing else is a reason to touch the file.
- Never record history. The map has no room for what was renamed, split, deprecated or shipped.
- Keep every summary to one line inside the cap.
pnpm verifychecks it in the docs lane, and the pressure that creates is deliberate, since anything longer belongs on the pages an entry points at. - Point
codeat roots that exist. A stale path fails the same lane.
Where each concept lives in code
Section titled “Where each concept lives in code”pnpm verify checks every path below against the tree, so a file that moves without its line here turns the gate red. No lane can check whether the description beside a path still names what that file does, and that holds by the rule that a behaviour and its entry here move in the same commit.
- Wire protocol, the native
Executorcontract and its specification:packages/protocol/src/protocol.ts, the executor protocol - Provider name rules and the reserved sets:
packages/protocol/src/provider-name.ts - Native remote executor, session rotation and reconnect:
packages/cloudflare-codemode/src/remote-executor.ts,packages/cloudflare-codemode/src/session.ts - The codemode dialect adapter, one facade over both transports:
packages/cloudflare-codemode/src/codemode-executor.ts - Execution planning of the dialect’s providers and connectors:
packages/cloudflare-codemode/src/execution-plan.ts - Native in-process executor over the pool:
packages/executor/src/executor/isolate-executor.ts - Raw model output normalized into the entry contract form:
packages/cloudflare-codemode/src/normalize.ts - Caller-side result capping:
packages/executor/src/executor/result-limiter.ts - Tool name sanitizing of the dialect:
packages/cloudflare-codemode/src/tool-name.ts - Typed error taxonomy carried by
codeacross boundaries:packages/protocol/src/errors.ts - Pool composition, admission and dispatch:
packages/executor/src/pool/pool.ts - Bounded queue and per-worker load accounting:
packages/executor/src/pool/queue.ts - Child supervision, spawn, heartbeat and crash handling:
packages/executor/src/pool/supervisor.ts - Child process entry point:
packages/executor/src/pool/child-entry.ts - IPC transport and the parent-to-child contract:
packages/executor/src/pool/ipc-transport.ts,packages/executor/src/pool/protocol.ts - Worker retirement and drain rules:
packages/executor/src/pool/lifecycle.ts - Published pool events and the OpenTelemetry attachment:
packages/executor/src/pool/events.ts,packages/executor/src/pool/instrumentation.ts - Sandbox composition, one execution end to end:
packages/executor/src/sandbox/sandbox.ts - Isolate creation and disposal:
packages/executor/src/sandbox/isolate.ts - Installed capabilities:
packages/executor/src/sandbox/console.ts,packages/executor/src/sandbox/timers.ts,packages/executor/src/sandbox/fetch.ts,packages/executor/src/sandbox/runtime.ts - The standard library the sandbox installs, one module per governing standard:
packages/executor/src/sandbox/runtime/ - The Web IDL binding every interface passes through:
packages/executor/src/sandbox/runtime/webidl.ts,packages/executor/src/sandbox/runtime/converters.ts - Where the standard library answers differently from the standard that governs it: Runtime deviations
- Generated runtime bundle and its generator:
packages/executor/src/sandbox/runtime-bundle.generated.ts,tools/executor/generate-runtime-bundle.ts - Module linking, import lifting and the CommonJS wrapper:
packages/executor/src/sandbox/modules.ts,packages/protocol/src/imports.ts,packages/executor/src/sandbox/cjs.ts - Which module a specifier names, for the linker and for require alike:
packages/executor/src/sandbox/resolver.ts - Tool dispatch bridge and the provider proxies the model calls:
packages/executor/src/sandbox/dispatch.ts,packages/executor/src/sandbox/proxy-source.ts - Binary-safe dispatch codec:
packages/protocol/src/codec.ts,packages/executor/src/sandbox/codec.ts - Timeout layering across the synchronous and asynchronous phases:
packages/executor/src/sandbox/timeout.ts - Retained executions and the handles a host invokes:
packages/executor/src/sandbox/handles.ts - The bridge that performs webcrypto operations on the host:
packages/executor/src/sandbox/crypto.ts - WebSocket listener and connection policy:
packages/executor/src/server/listener.ts - Per-execution bridge between the caller session and the pool:
packages/executor/src/server/bridge.ts - Liveness and readiness probes and the metrics scrape:
packages/executor/src/server/probes.ts - Connection metrics and the per-identity request span:
packages/executor/src/server/instrumentation.ts - Span settling shared by the pool and the server:
packages/executor/src/spans.ts - Daemon command line, its settings and their provenance:
apps/daemon/src/cli - Admin app and the client the command line calls it with:
apps/daemon/src/admin.ts - Sandbox check, one real job end to end:
apps/daemon/src/check.ts - Daemon composition, lifecycle events and the process entry:
apps/daemon/src/daemon.ts,apps/daemon/src/bin.ts - Telemetry and logging composed for the daemon:
apps/daemon/src/telemetry.ts,apps/daemon/src/logger.ts - Container image and the manifests that run it:
apps/daemon/Dockerfile,deploy - Differential compatibility suite and its reference harness:
apps/executor-testbed/src/compatibility,apps/executor-testbed/src/reference - Property suite and the latency benchmark of the two run bounds:
apps/executor-benchmark/src, the benchmark - Quality gate and repository scripts:
tools/ci/verify.ts, described in Tooling