POST /processes. It owns:
- Code execution. Transpiling, sandboxing, and running submitted code.
- Runtime bindings. Exposing the
cyrnel.*API (or whatever shape it prefers) so user code can discover and invoke tools. - Output capture. Streaming stdout, stderr, and structured output back to the host through callbacks.
- Cancellation and timeouts. Honouring
kill(eid)and respectinginput.envConfig.timeoutMsfor an execution-level sandbox deadline. The host separately enforces an API-level timeout viakill(eid)when thetimeoutMsfield onPOST /processeselapses.
EnvironmentModule interface.
Lifecycle
setup({ bindings })is called when the environment is activated.bindingsis theEnvironmentBindingscallback surface back into the host (discovery, invocation, state, output).execute(input)is called for each process.input.eidis the process id; the environment must use it on every callback so the host can route stdout/stderr/output to the right process.input.codeis the source code to run.input.envConfigis aRecord<string, unknown>of per-execution configuration forwarded from the request. Each environment module documents its acceptedenvConfigkeys.kill(eid)interrupts a running or queued execution. Should return promptly.generateDocs()returns a Markdown string describing the environment’s globals. Surfaced byGET /environment/docs.generateToolDocs(input)returns Markdown describing how to call a single tool inside this environment. Surfaced byGET /tools/:serviceId/:toolId/docs.teardown()is called when the environment is deactivated.
Timeout model
The API-leveltimeoutMs field on POST /processes is enforced by the
host: the platform calls kill(eid) if the process exceeds the deadline.
The environment module never sees this value directly; it only
participates through kill(eid).
input.envConfig.timeoutMs is an optional per-execution override that
the environment module may use for its own internal deadlines (e.g. the
TypeScript isolate’s sandbox timeout). The two levels are independent;
the shorter of the two always wins.
Active environment vs draining
Only one environment is active at a time, the one whosemodules row has
enabled = true. Switching environments or disabling the active module
moves it to a draining state: no new executions are dispatched to it,
but in-flight ones run to completion. Once the last execution finishes, cyrnel
calls teardown() on it.
This means:
- An environment’s
executemay be called after another environment is active. Implementations must not assume “active” and “currently running executions” are the same set. killmay be called during draining. Honour it.
Security Model
Environment modules own the security boundary user code runs inside. Their job, more than anything else, is to make sure user code cannot do anything the environment didn’t intend to expose. For the bundledtypescript-ivm:
- Code runs in an
isolated-vmisolate with a hard memory limit. - The isolate has no module loader, no filesystem, no network, no Node built-ins. The only escape hatches are the references cyrnel installs.
- Timeouts terminate the isolate; the worker slot is recreated rather than reused.
Opt-in sandbox bindings
By default the isolate exposes a bare ECMAScript runtime: nofetch, no
setTimeout, no TextEncoder, no btoa, no crypto, no URL. Operators
can selectively enable web-platform APIs via the module’s bindings
config, each a boolean:
All flags default to
false. Bindings are resolved once at environment
activation - an agent cannot enable them per-execution. Timers are
host-supervised (max delay 60000 ms, max 16 concurrent, cleared when the
execution function returns), and random values come from the host via
node:crypto. The environment’s docs (GET /environment/docs) list which
bindings are enabled for the active config.
If you write a custom environment, the relevant questions are:
- What can user code reach? (Network? Filesystem? Native code?)
- Can a malicious process exhaust resources? (CPU loops, memory, file descriptors.)
- Can one execution affect another? (Shared globals, persisted state.)
- How are credentials exposed? (Adapter modules see secrets; environments should not.)
Authoring
See Writing a custom environment module for the file layout, a skeleton, and a full working example.EnvironmentModule covers the interface
contract in full, and EnvironmentBindings
documents the callback surface.