How to Configure the AxJSRuntime for Secure JavaScript Sandboxing in Ax
The AxJSRuntime provides permission-based sandboxing that blocks all dangerous globals by default and allows selective capability grants via the AxJSRuntimePermission enum.
The Ax framework (ax-llm/ax) includes a built-in JavaScript interpreter called AxJSRuntime that powers the Repl-like-Model (RLM) subsystem. This runtime executes model-generated code inside a locked-down sandbox that isolates execution from the host process through a deny-by-default permission system.
Core Sandbox Architecture
Permission-Based Capability Model
The runtime implements least-privilege security through the AxJSRuntimePermission enum defined in src/ax/funcs/jsRuntime.ts. Rather than starting with full access and restricting, it begins with zero capabilities and requires explicit opt-in for every potentially dangerous operation.
export enum AxJSRuntimePermission {
/** fetch, XMLHttpRequest, WebSocket, EventSource */ NETWORK = 'network',
/** indexedDB, caches */ STORAGE = 'storage',
/** importScripts */ CODE_LOADING = 'code-loading',
/** BroadcastChannel */ COMMUNICATION = 'communication',
/** performance */ TIMING = 'timing',
/** Worker, SharedWorker (brings all above capabilities) */ WORKERS = 'workers',
}
Source: jsRuntime.ts – Permission enum
Constructor Configuration Options
When instantiating the runtime, you pass an options object to the constructor. The most critical fields for sandboxing control are:
permissions?: readonly AxJSRuntimePermission[]– The allow-list of capabilities.allowUnsafeNodeHostAccess?: boolean– Dangerous flag that grants access to Node.js globals likeprocessandrequire. Defaults tofalse.outputMode?: 'return' | 'stdout'– Determines whether results are collected fromreturnstatements orconsole.logoutput.captureConsole?: boolean– Automatically capturesconsole.*output whenoutputModeis'stdout'.
The constructor signature in src/ax/funcs/jsRuntime.ts (lines 11-28) accepts:
constructor(options?: {
timeout?: number;
permissions?: readonly AxJSRuntimePermission[];
outputMode?: AxJSRuntimeOutputMode;
captureConsole?: boolean;
allowUnsafeNodeHostAccess?: boolean;
nodeWorkerPoolSize?: number;
debugNodeWorkerPool?: boolean;
})
Worker-Based Isolation
The runtime creates a Web-Worker-compatible session via createSession. In browser environments it uses native Worker instances; in Node.js it falls back to worker_threads. During initialization, the worker receives the permission list through an init message, and the sandbox implementation inside worker.runtime.ts enforces those restrictions by deleting unauthorized globals from the worker's environment.
Available Permissions and Security Implications
- NETWORK – Grants access to
fetch,XMLHttpRequest,WebSocket, andEventSourcefor external data retrieval. - STORAGE – Enables
indexedDBandcachesfor client-side data persistence. - CODE_LOADING – Allows
importScriptsfor dynamically loading additional code. - COMMUNICATION – Permits
BroadcastChannelfor cross-context messaging. - TIMING – Exposes the
performanceobject for high-resolution timing. - WORKERS – Enables
WorkerandSharedWorkerinstantiation. Warning: This implicitly grants all other capabilities inside spawned sub-workers because they initialize with a fresh global environment containing all permissions.
Critical Safety Note: The allowUnsafeNodeHostAccess option should never be enabled in multi-tenant or untrusted scenarios. When set to true, sandboxed code can access the host Node.js process, filesystem, and native modules.
Configuration Examples
Minimal Sandbox (Default)
Omitting the permissions array creates the tightest sandbox, blocking all external I/O.
import { AxJSRuntime, agent } from '@ax-llm/ax';
const runtime = new AxJSRuntime(); // No permissions = safest sandbox
const myAgent = agent(
'input:string -> output:string',
{ runtime }
);
Network and Timing Access
Enable fetch and performance.now() for agents that need to download data and measure execution time:
import {
AxJSRuntime,
AxJSRuntimePermission,
agent,
} from '@ax-llm/ax';
const runtime = new AxJSRuntime({
permissions: [
AxJSRuntimePermission.NETWORK, // Allow fetch / XMLHttpRequest
AxJSRuntimePermission.TIMING, // Allow performance.now()
],
});
const analyzer = agent(
'context:string, query:string -> answer:string, evidence:string[]',
{
runtime,
maxSteps: 10,
}
);
Source: examples/rlm.ts – Runtime configuration
Storage and Sub-Workers (Advanced)
Persist intermediate results with IndexedDB while using parallel workers:
const runtime = new AxJSRuntime({
permissions: [
AxJSRuntimePermission.STORAGE, // IndexedDB / cache access
AxJSRuntimePermission.WORKERS, // Sub-workers (implies all capabilities)
],
outputMode: 'return', // Collect via return value
});
Unsafe Node Host Access (Internal Only)
For trusted internal tooling where sandboxed code must interact with the host Node.js environment:
const runtime = new AxJSRuntime({
allowUnsafeNodeHostAccess: true, // Exposes require, process, etc.
});
Never use this configuration for untrusted or AI-generated code.
Key Implementation Files
| File | Purpose |
|---|---|
src/ax/funcs/jsRuntime.ts |
Core AxJSRuntime implementation, permission enum, and constructor logic. |
src/examples/rlm.ts |
Real-world demonstration of passing permission arrays to the runtime. |
src/ax/funcs/worker.runtime.ts |
Worker-side code that enforces permission restrictions by manipulating globals. |
src/ax/funcs/jsRuntime.test.ts |
Test suite verifying sandbox restrictions and permission boundaries. |
Summary
- AxJSRuntime uses a deny-by-default security model where all dangerous globals are blocked unless explicitly permitted.
- Configure capabilities through the
permissionsarray using theAxJSRuntimePermissionenum. - The runtime executes code inside isolated Web Workers (or Node.js
worker_threads) to separate execution contexts from the host process. - Enabling
AxJSRuntimePermission.WORKERSimplicitly grants all other permissions to any spawned sub-workers. - The
allowUnsafeNodeHostAccessflag bypasses all protections and should only be used for trusted internal scripts.
Frequently Asked Questions
What permissions does AxJSRuntime block by default?
By default, the runtime blocks all potentially unsafe globals including fetch, indexedDB, importScripts, WebSocket, and BroadcastChannel. If you instantiate new AxJSRuntime() without a permissions array, the sandbox provides only pure computational access with no network, storage, or timing capabilities.
How do I enable network requests in the sandbox?
Import AxJSRuntimePermission.NETWORK and include it in the permissions array: new AxJSRuntime({ permissions: [AxJSRuntimePermission.NETWORK] }). This grants access to fetch, XMLHttpRequest, WebSocket, and EventSource while maintaining restrictions on file system and host process access.
Is AxJSRuntime safe for running untrusted AI-generated code?
Yes, when configured with the default settings (no permissions and allowUnsafeNodeHostAccess: false), the runtime provides a secure sandbox suitable for executing untrusted code. The worker-based isolation prevents access to the host process, and the permission system blocks external I/O. Never enable allowUnsafeNodeHostAccess for untrusted inputs.
What is the difference between the WORKERS permission and other permissions?
While other permissions grant specific capabilities like network or storage access, AxJSRuntimePermission.WORKERS enables the creation of sub-workers via new Worker() or new SharedWorker(). Because these sub-workers initialize with a fresh global environment, they implicitly receive all other capabilities (network, storage, timing, etc.) regardless of the parent runtime's permission settings.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →