Skip to content

Chapter 11 · Sandboxes: Safe Execution Environments ​

Chapter goals: Understand "an agent has no environment unless you give it one"; master useSandbox(), the local local() sandbox, and the differences between virtual and remote container sandboxes; learn to design安全边界.

11.1 What a Sandbox Gives an Agent ​

An agent has at most one execution environment, and by default has none — without calling useSandbox() it can only do纯 text对话. Mount a sandbox and the agent's toolset一次性获得:

text
read / write / edit / bash / grep   ← file and shell capabilities
typescript
// agents/researcher.ts
// An agent that only reads the web, never touches files: no sandbox
'use agent';
import { useModel } from '@flue/runtime';

export function Researcher() {
  useModel('anthropic/claude-sonnet-4-6');
  return 'You are a research assistant; answer solely from materials in the conversation.';
}
typescript
// agents/coder.ts
// An agent that needs to read/write code and run commands: mount the local sandbox
'use agent';
import { useModel, useSandbox } from '@flue/runtime';
import { local } from '@flue/runtime/node';

export function Coder() {
  useModel('anthropic/claude-sonnet-4-6');
  useSandbox(local()); // Bind to the host filesystem
  return 'You are a bug-fixing engineer; you can directly modify repo files and run tests.';
}

11.2 Three Sandbox Forms ​

Flue provides threegranularity-increasing isolation modes:

FormAPIIsolation LevelTypical Scenario
Virtual (in-memory)Built-in virtual sandboxNo system callsTeaching, pure-text file experiments
Locallocal()Host permissionsPersonal dev, trusted scripts
Remote containerRemote provider (e.g., Daytona)Container/microVM isolationProduction, running untrusted code
typescript
// Virtual sandbox: fake filesystem in memory
'use agent';
import { useSandbox } from '@flue/runtime';
import { virtual } from '@flue/runtime/sandbox';

export function NoteTaker() {
  useModel('anthropic/claude-sonnet-4-6');
  // In-memory virtual FS: disappears on进程 exit; never touches real disk
  useSandbox(virtual({
    files: {
      'notes.md': '# Meeting Notes\n- TBD',
    },
  }));
  return 'Maintain a Markdown note for the user.';
}
typescript
// Remote container sandbox: toss untrusted execution into an isolated container
'use agent';
import { useSandbox } from '@flue/runtime';
import { daytona } from '@flue/runtime/daytona'; // Using Daytona as an example

export function UntrustedRunner() {
  useModel('anthropic/claude-sonnet-4-6');
  // Each task runs in a remote container; crashes or malicious behavior cannot affect the host
  useSandbox(daytona({ image: 'node:22-slim', timeoutMinutes: 30 }));
  return 'Run user-submitted code in the sandbox and report the output.';
}

11.3 Environment Changes Over Lifecycle ​

The docs强调 "an agent's environment can change over its life" — the sandbox is not一次性的. Common pattern: use local() during development for rapid iteration, switch to a remote container in production.

typescript
// Select sandbox implementation based on deployment environment
'use agent';
import { useSandbox } from '@flue/runtime';
import { local } from '@flue/runtime/node';
import { daytona } from '@flue/runtime/daytona';

export function Worker() {
  const env = process.env.DEPLOY_ENV ?? 'dev';

  if (env === 'prod') {
    // Production: container isolation + timeout protection
    useSandbox(daytona({ image: 'node:22-slim', timeoutMinutes: 60 }));
  } else {
    // Development: use the host directly; convenient for debugging
    useSandbox(local());
  }
  return 'A通用 worker that auto-selects the execution environment by DEPLOY_ENV.';
}

11.4 Designing Safety Boundaries ​

The sandbox是安全边界的第一道墙. When designing, ask three questions:

  1. What is the minimum capability this agent needs? A read-only researcher should not have write;
  2. Is the input it processes trusted? Processing user-submitted code requires a container;
  3. How big is the blast radius? local()'s boundary is your user权限 — don't use it in production.
typescript
// Boundary example: minimum-privilege组合
'use agent';
import { useSandbox, useTool } from '@flue/runtime';
import { local } from '@flue/runtime/node';
import { fetchDocs } from '../tools/docs.ts';

export function Auditor() {
  // Audit agent: can read code to find issues, but has no write tools
  useSandbox(local());
  // Custom tools also follow least-privilege: fetchDocs only issues GET internally
  useTool(fetchDocs);
  return 'Audit the current repo for security issues; report only, do not modify.';
}

Comparison with Pi

Pi officially states it自带无权限系统 and inherits the launcher's permissions by default, recommending容器化 as fallback. Flue has made this layer框架内建 (virtual/local/remote), but picking the wrong form is equally危险 — local() does not equal安全.

11.5 Chapter Summary ​

  • Default agents have no environment; useSandbox() grants read/write/edit/bash/grep;
  • Three forms: virtual (in-memory), local() (host), remote container (Daytona etc.);
  • Environment can change over the agent's life — switching by DEPLOY_ENV is a common pattern;
  • Three safety-boundary questions: minimum capability? Trusted input? Blast radius?

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. What happens if you do not call useSandbox() in a Flue agent?

2. Which sandbox form is appropriate for running untrusted user-submitted code?

3. What does the "environment can change over its life" principle enable?

4. Why is local() considered dangerous in production?

🛠️ Hands-On Exercises ​

  1. Write an agent with a virtual sandbox; verify it can read/write a fake file but cannot access the real filesystem.
  2. Set up a Daytona remote container sandbox; run a test that crashes inside the container and confirm the host is unaffected.
  3. Write an agent that switches between local() and daytona() based on DEPLOY_ENV; verify the correct sandbox is mounted in each environment.

Sandboxes mastered. Next, Durability: persistence and recovery.