Skip to main content
Enclave is FrontMCP’s secure execution environment for running untrusted JavaScript code. It provides a defense-in-depth security model that combines AST validation (via ast-guard), code transformation, and runtime sandboxing to safely execute model-generated code.

AST Validation

Block dangerous constructs before execution using ast-guard’s AgentScript preset

Code Transformation

Automatically transform code for safe execution with proxied functions and loop limits

Runtime Sandboxing

Execute in isolated Node.js vm context with controlled globals and resource limits

When to Use Enclave

Enclave is designed for scenarios where you need to execute JavaScript code from untrusted sources:
  • LLM-generated code - Execute code written by AI models safely
  • User-provided scripts - Run user scripts in a controlled environment
  • Plugin/extension systems - Allow third-party code to run securely
  • Workflow automation - Execute orchestration logic with tool access
Enclave is used internally by the CodeCall Plugin to execute JavaScript execution plans. You can also use it directly for custom use cases.

Installation

Enclave is available as a separate package:
npm install enclave-vm

Quick Start

import { Enclave } from 'enclave-vm';

// Create an enclave with a tool handler
const enclave = new Enclave({
  timeout: 5000, // 5 second timeout
  maxToolCalls: 50, // Max 50 tool calls
  maxIterations: 10000, // Max 10K loop iterations
  toolHandler: async (toolName, args) => {
    // Handle tool calls from the script
    console.log(`Tool called: ${toolName}`, args);
    return { result: 'data' };
  },
});

// Execute AgentScript code
const code = `
  const users = await callTool('users:list', { limit: 10 });
  const filtered = users.filter(u => u.active);
  return filtered.length;
`;

const result = await enclave.run(code);

if (result.success) {
  console.log('Result:', result.value);
  console.log('Stats:', result.stats);
} else {
  console.error('Error:', result.error);
}

// Clean up
enclave.dispose();

Security Level Presets

Enclave provides pre-configured security profiles that balance functionality against risk:
import { Enclave } from 'enclave-vm';

// Use STRICT for untrusted AI-generated code
const strictEnclave = new Enclave({ securityLevel: 'STRICT' });

// Use STANDARD for internal tools (default)
const standardEnclave = new Enclave({ securityLevel: 'STANDARD' });

// Override specific values from the preset
const customEnclave = new Enclave({
  securityLevel: 'SECURE',
  timeout: 20000,  // Override SECURE's 15s default
});

Security Level Comparison

SettingSTRICTSECURESTANDARDPERMISSIVE
timeout5s15s30s60s
maxIterations1,0005,00010,000100,000
maxToolCalls10501001,000
maxConsoleCalls1005001,00010,000
maxConsoleOutputBytes64KB256KB1MB10MB
sanitizeStackTracesYESYESNONO
blockTimingAPIsYESNONONO
allowUnboundedLoopsNONOYESYES
unicodeSecurityCheckYESYESNONO
Use STRICT for any untrusted code, including AI-generated scripts from external sources or user-submitted scripts.

Defense-in-Depth Security Model

Enclave uses a four-layer security approach:

Layer 1: AST Validation

Before any code runs, Enclave validates the AST using ast-guard’s AgentScript preset:
// These are automatically blocked:
eval('code');                    // ❌ eval blocked
new Function('return 1')();       // ❌ Function constructor blocked
setTimeout(() => {}, 100);        // ❌ setTimeout blocked
process.exit();                   // ❌ process access blocked
window.location;                  // ❌ window access blocked
this.constructor;                 // ❌ this access blocked
The AgentScript preset blocks:
CategoryBlocked Constructs
Eval-likeeval, Function, setTimeout, setInterval
Globalsprocess, require, window, document, global, globalThis
Context accessthis, self, parent, top, frames
Prototype manipulation__proto__, constructor, prototype access

Layer 2: Code Transformation

Valid code is transformed for safe execution:
// Original code:
const users = await callTool('users:list', {});
for (const user of users) {
  console.log(user.name);
}

// Transformed code:
async function __ag_main() {
  const users = await __safe_callTool('users:list', {});
  __safe_forOf(users, (user) => {
    __safe_console.log(user.name);
  });
}
Transformations include:
  • Main wrapper: Wrap code in async function __ag_main() for top-level await
  • Safe callTool: Transform callTool__safe_callTool (proxied through Enclave)
  • Safe loops: Transform for, while, do-while → safe versions with iteration limits
  • Safe console: Transform console__safe_console (captured for logging with rate limiting)

Layer 3: Runtime Sandboxing

Code executes in an isolated Node.js vm context:
  • Isolated context: Fresh context with no access to host environment
  • Controlled globals: Only whitelisted globals available (Math, JSON, Array, etc.)
  • Resource limits: Timeout, iteration count, tool call limits, and console rate limits enforced
  • Stack sanitization: Error stack traces sanitized to prevent information leakage

Layer 4: Reference Sidecar (Optional)

When enabled, large data is handled separately:
  • Large data extraction: Strings exceeding the threshold are stored in sidecar storage
  • Reference tokens: Large strings are replaced with safe reference tokens (__ref_abc123)
  • Lazy resolution: Data is only resolved when explicitly accessed
  • Composite blocking: String concatenation with references can be blocked (allowComposites: false)

Configuration Options

timeout
number
default:"30000"
Maximum execution time in milliseconds (default 30 seconds)
maxToolCalls
number
default:"100"
Maximum number of tool calls allowed per execution
maxIterations
number
default:"10000"
Maximum loop iterations allowed (prevents infinite loops)
toolHandler
function
Async function that handles callTool() invocations from the script. Signature: (toolName: string, args: Record<string, unknown>) => Promise<unknown>
globals
object
Additional globals to make available in the script context
validate
boolean
default:"true"
Whether to validate code with ast-guard before execution
transform
boolean
default:"true"
Whether to transform code before execution
allowFunctionsInGlobals
boolean
default:"false"
Allow functions in the globals object. Required when providing callback functions or utilities.
maxConsoleOutputBytes
number
Maximum total console output in bytes. Prevents I/O flood attacks via excessive logging. Defaults vary by security level: STRICT=64KB, SECURE=256KB, STANDARD=1MB, PERMISSIVE=10MB.
maxConsoleCalls
number
Maximum number of console calls allowed. Prevents I/O flood attacks via rapid-fire logging. Defaults vary by security level: STRICT=100, SECURE=500, STANDARD=1000, PERMISSIVE=10000.
sidecar
object
Configuration for handling large data via reference tokens

Reference Sidecar

The sidecar is a powerful feature for handling large data in AgentScript without embedding it in the script. This keeps script size small for reliable AST validation while allowing tools to return large datasets.

How It Works

  1. Extraction: When a tool returns data with large strings (> extractionThreshold), those strings are stored in the sidecar and replaced with reference tokens (__ref_abc123)
  2. Lazy Resolution: When script code accesses a reference token, it’s resolved just-in-time to the actual data
  3. Safe Property Access: Only explicit property accesses trigger resolution, preventing data exfiltration
const enclave = new Enclave({
  toolHandler: async (name, args) => {
    if (name === 'documents:get') {
      // Returns a 2MB document - automatically stored in sidecar
      return { content: '...2MB of text...' };
    }
  },
  sidecar: {
    enabled: true,
    extractionThreshold: 1024,    // Extract strings > 1KB
    allowComposites: false,        // Block: ref + "_suffix" (security)
  },
});

const result = await enclave.run(`
  const doc = await callTool('documents:get', { id: 'doc-123' });
  // doc.content is a reference token, resolved on access
  return { wordCount: doc.content.split(' ').length };
`);

Security: allowComposites

The allowComposites: false setting (default) blocks string concatenation with reference tokens:
// When allowComposites: false (default, secure)
const result = ref + "_suffix";  // ❌ BLOCKED - prevents prototype pollution

// When allowComposites: true (less secure)
const result = ref + "_suffix";  // ✅ ALLOWED - use with caution
Keep allowComposites: false unless you specifically need string concatenation with large data.

AI Scoring Gate

The Scoring Gate adds semantic security analysis that detects attack patterns beyond static AST validation:
  • Data exfiltration - list→send or query→export sequences
  • Excessive access - High limits, wildcard queries
  • Fan-out attacks - Tool calls inside loops
  • Sensitive data access - Passwords, tokens, PII fields
import { Enclave } from 'enclave-vm';

// Rule-based scorer (~1ms latency, zero dependencies)
const enclave = new Enclave({
  scoringGate: {
    scorer: 'rule-based',
    blockThreshold: 70,     // Score >= 70 blocks execution
    warnThreshold: 40,      // Score >= 40 logs warning
    failOpen: true,         // Allow if scoring fails (default)
  },
});

// External API scorer (best detection, ~100ms latency)
const enclaveWithApi = new Enclave({
  scoringGate: {
    scorer: 'external-api',
    externalApi: {
      endpoint: 'https://api.example.com/score',
      apiKey: process.env.SCORING_API_KEY,
      timeoutMs: 5000,
      retries: 1,
    },
  },
});

Scorer Types

TypeLatencyDependenciesDetection
disabled0msNoneNone
rule-based~1msNoneGood
local-llm~5-10msModel downloadBetter
external-api~100msNetworkBest

Detection Rules

The rule-based scorer detects these patterns:
RuleScoreDescription
SENSITIVE_FIELD35Queries password/token/secret fields
EXCESSIVE_LIMIT25limit > 10,000
WILDCARD_QUERY20query=”*” or filter=
LOOP_TOOL_CALL25callTool inside for/for-of loop
EXFIL_PATTERN50list→send or query→export sequence
EXTREME_VALUE30Numeric arg > 1,000,000
DYNAMIC_TOOL20Variable tool name (not static string)
BULK_OPERATION15Tool name contains bulk/batch/all

Caching

Results are cached by code hash (default: 5 minutes, 1000 entries):
const enclave = new Enclave({
  scoringGate: {
    scorer: 'rule-based',
    cache: {
      enabled: true,
      ttlMs: 300000,    // 5 minutes
      maxEntries: 1000,
    },
  },
});

Worker Pool Adapter

For OS-level memory isolation, use the worker threads adapter:
import { Enclave } from 'enclave-vm';

const enclave = new Enclave({
  adapter: 'worker_threads',
  workerPoolConfig: {
    minWorkers: 2,
    maxWorkers: 8,
    memoryLimitPerWorker: 256 * 1024 * 1024, // 256MB
  },
});

Worker Pool Features

  • Pool management - Auto-scaling with min/max workers
  • Memory monitoring - Workers recycled when exceeding limits
  • Hard halt - Force terminate via worker.terminate()
  • Rate limiting - Message flood protection
  • Dual-layer sandbox - Worker thread + VM context isolation

Worker Pool Presets

SettingSTRICTSECURESTANDARDPERMISSIVE
maxWorkers481632
memoryLimitPerWorker64MB128MB256MB512MB
maxExecutionsPerWorker1005001,0005,000
maxQueueSize2050100500
maxMessagesPerSecond1005001,0005,000

Worker Pool Configuration

workerPoolConfig.minWorkers
number
default:"2"
Minimum warm workers to keep in the pool
workerPoolConfig.maxWorkers
number
default:"os.cpus().length"
Maximum workers in the pool
workerPoolConfig.memoryLimitPerWorker
number
default:"128MB"
Memory limit per worker (workers exceeding this are recycled)
workerPoolConfig.maxExecutionsPerWorker
number
default:"1000"
Executions before a worker is recycled (prevents memory leaks)
workerPoolConfig.maxQueueSize
number
default:"100"
Maximum pending executions in the queue
workerPoolConfig.maxMessagesPerSecond
number
default:"1000"
Rate limit for messages from a single worker (prevents flooding)

Execution Results

Enclave returns a structured result with success/error status and execution stats:
interface ExecutionResult<T> {
  success: boolean;
  value?: T;              // Result value (if success)
  error?: {               // Error details (if failed)
    name: string;
    message: string;
    code: string;
    stack?: string;
  };
  stats: {
    duration: number;      // Execution time in ms
    toolCallCount: number; // Number of tool calls made
    iterationCount: number; // Number of loop iterations
  };
}

Error Codes

CodeMeaningAction
VALIDATION_ERRORAST validation failedFix the code - blocked construct used
EXECUTION_ERRORRuntime error in scriptFix script logic
TIMEOUTExecution exceeded timeoutOptimize or increase timeout
TOOL_ERRORTool call failedCheck tool input/availability

Advanced Usage

Custom Globals

Provide custom globals for scripts to access:
const enclave = new Enclave({
  toolHandler: async (name, args) => { /* ... */ },
  globals: {
    // Custom read-only context
    context: {
      userId: 'user-123',
      tenantId: 'tenant-456',
    },
    // Custom utility function
    formatDate: (date: Date) => date.toISOString(),
  },
});

const code = `
  const userId = context.userId;
  const timestamp = formatDate(new Date());
  return { userId, timestamp };
`;

const result = await enclave.run(code);

One-Shot Execution

For simple cases, use the convenience function:
import { runAgentScript } from 'enclave-vm';

const result = await runAgentScript(`
  return Math.max(1, 2, 3);
`, {
  timeout: 1000,
});

console.log(result.value); // 3

Tool Handler Integration

Integrate with your existing tool system:
import { Enclave } from 'enclave-vm';
import { ToolRegistry } from './tools';

const enclave = new Enclave({
  timeout: 10000,
  toolHandler: async (toolName, args) => {
    // Look up tool in registry
    const tool = ToolRegistry.get(toolName);
    if (!tool) {
      throw new Error(`Unknown tool: ${toolName}`);
    }

    // Execute with your pipeline (auth, logging, etc.)
    return tool.execute(args);
  },
});

Security Considerations

While Enclave provides strong security guarantees, it should be used as part of a defense-in-depth strategy. Always:
  • Validate tool inputs before execution
  • Limit what tools are available to scripts
  • Monitor execution for anomalies
  • Keep Enclave and ast-guard updated

What Enclave Protects Against

  • Code injection - Blocked by AST validation
  • Infinite loops - Limited by maxIterations
  • Resource exhaustion - Limited by timeout and maxToolCalls
  • I/O flood attacks - Limited by maxConsoleOutputBytes and maxConsoleCalls
  • Global access - Blocked by AST validation and isolated context
  • Prototype pollution - Blocked by AST validation
  • Information leakage - Stack traces sanitized

What Enclave Does NOT Protect Against

  • Tool abuse - Scripts can call allowed tools; limit what’s available
  • Algorithmic complexity - Scripts can run O(n²) algorithms within limits
  • Memory exhaustion - Large arrays/objects within timeout
  • Side effects - Tool calls have real effects; use read-only tools where possible

Integration with CodeCall

The CodeCall Plugin uses Enclave internally:
// CodeCall plugin configuration maps to Enclave
CodeCallPlugin.init({
  enclave: {
    timeoutMs: 5000,
    maxToolCalls: 100,
    maxIterations: 10000,
    allowConsole: true,
  },
});

// Equivalent to:
new Enclave({
  timeout: 5000,
  maxToolCalls: 100,
  maxIterations: 10000,
  globals: {
    console: { log: ..., warn: ..., error: ... },
  },
});

Resources