Sandbox
QuickJS by itself has no Node builtins. ferridriver adds a curated, sandbox-safe subset so real npm packages and modern web code work. This page lists what is present, what is not, and what is gated.
process
Always available (no authority, real values):
Absent on purpose
process.exit— throws (a script must never kill the server)process.binding,process.dlopen,process.kill,process.chdir,process.setuid, …- Anything that would grant ambient authority not declared in the capability manifest.
fetch
Web-standard fetch(input, init?) with the WHATWG globals Headers,
Request, Response, AbortController, AbortSignal, Blob,
FormData, ReadableStream — constructible; instanceof works.
Headers
Spec-compliant: case-insensitive, , -combined, set-cookie separate
getSetCookie(), real iterators,forEach.
Request
new Request(url|Request, init?) carries url / method / headers /
redirect / credentials / bodyUsed. Accepted by fetch.
Known subset: a signal set on a Request instance is not yet
forwarded — pass it via init.signal instead.
Response
status / ok / statusText / url / redirected / type /
bodyUsed / headers. Single-use text() / json() /
arrayBuffer(). clone(). Static Response.json() / error() /
redirect().
Known subset: clone() of a not-yet-read streamed Response
throws (no stream tee yet).
Response.body — streaming
Response.body is a ReadableStream that pulls chunks live off the
socket. A large or streamed body is not fully buffered.
text() / json() / arrayBuffer() drain it on demand.
new ReadableStream({ start(c) { c.enqueue(x); c.close(); } }) works.
Known subset: no pull, no tee, no BYOB readers.
AbortController / AbortSignal
Standard. controller.abort(reason?), signal.aborted,
signal.reason, signal.throwIfAborted(), signal.onabort,
signal.addEventListener("abort", ...), AbortSignal.abort() /
AbortSignal.timeout(ms) / AbortSignal.any([...]).
fetch(url, { signal }) rejects an already-aborted call before any I/O
and cancels an in-flight request.
Blob, FormData
Both accepted as fetch bodies. Blob sends its bytes + type;
FormData is sent as multipart/form-data.
Known subset: FormData iteration is via entries() / forEach
returning arrays (not native iterators).
One HTTP core
fetch runs on the same HTTP core as the request global — so
cookies / sessions are shared and any
allow.net restriction on a
tool's request binds fetch the same way (no second stack, no
bypass).
request (the Playwright-style API) stays; fetch is the standard
entry point. The Playwright page-network Request / Response (from
page.on(...), route, navigation) are unchanged but are not
global constructors — the bare Request / Response globals are the
fetch classes.
fs
Scoped file I/O bound to script_root:
Absolute paths, .., and symlink escapes are rejected.
artifacts
Dedicated output directory (artifacts_root from MCP config), for
results the agent wants to hand back to the caller:
Same sandbox rules as fs.
What is absent
require(),module,__dirname,__filename— no CommonJS at runtime (rolldown handlesrequirecalls at bundle time).- Node's
child_process,cluster,http,https,net,tls,dgram,dns,vm,worker_threads,crypto.createServer, … - Browser DOM globals (
window,document,localStorage, …) — these exist insidepage.evaluate, not in script scope.
See also
- Capabilities —
allow.commands,allow.net - State and sessions —
globalThis,vars, session lifetime run_scriptreference — globals and return shape