Chapter 11 · Sandboxes: Safe Execution Environments
Chapter goals: Understand "an agent has no environment unless you give it one"; master
useSandbox(), the locallocal()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一次性获得:
read / write / edit / bash / grep ← file and shell capabilities// 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.';
}// 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:
| Form | API | Isolation Level | Typical Scenario |
|---|---|---|---|
| Virtual (in-memory) | Built-in virtual sandbox | No system calls | Teaching, pure-text file experiments |
| Local | local() | Host permissions | Personal dev, trusted scripts |
| Remote container | Remote provider (e.g., Daytona) | Container/microVM isolation | Production, running untrusted code |
// 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.';
}// 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.
// 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:
- What is the minimum capability this agent needs? A read-only researcher should not have
write; - Is the input it processes trusted? Processing user-submitted code requires a container;
- How big is the blast radius?
local()'s boundary is your user权限 — don't use it in production.
// 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
- Write an agent with a virtual sandbox; verify it can read/write a fake file but cannot access the real filesystem.
- Set up a Daytona remote container sandbox; run a test that crashes inside the container and confirm the host is unaffected.
- 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.