Using the Flue Local Sandbox in CI/CD Environments with Host Filesystem Access

The Flue local sandbox (sandbox: 'local') binds directly to the host filesystem, allowing agents to read repository files and write build artifacts using native Node.js fs/promises APIs without virtual layer overhead.

When running Flue agents in continuous integration pipelines, you often need direct access to the build container's filesystem to analyze source code or persist generated outputs. The local sandbox option in the withastro/flue repository provides a direct bridge between the agent's execution environment and the host filesystem, bypassing the default in-memory isolation layer to match standard CI script behavior.

How the Local Sandbox Works in Flue

Flue agents execute inside a sandbox that isolates the model’s execution from the host environment. By default, the sandbox uses an in-memory “empty” filesystem (sandbox: 'empty'). When you select local sandbox mode, the system delegates all filesystem operations directly to the host Node.js implementation.

Direct Filesystem Delegation

All filesystem APIs (readFile, writeFile, stat, readdir, etc.) are delegated straight to Node’s fs/promises implementation through the createCwdSessionEnv helper in packages/sdk/src/sandbox.ts. This eliminates the need to copy files into a virtual layer before processing.

Host Shell Integration

Shell commands execute through the host’s shell with full access to process.env and the current working directory (cwd). No path remapping occurs—the paths you pass are the exact paths on the runner.

Zero Overhead Architecture

No intermediate “just‑bash” layer is inserted between the agent and the host. When the Flue CLI runs inside a container (Docker, GitHub Actions runner, GitLab CI job), the container provides the isolation boundary while the local sandbox simply uses that boundary directly.

Configuring the Local Sandbox in CI/CD

The sandbox choice is exposed via the AgentInit options defined in packages/sdk/src/types.ts:

export interface AgentInit {
  /** ...
   * - `'empty'` – in‑memory sandbox (default)
   * - `'local'` – direct host access
   * - SandboxFactory – custom remote sandbox connector
   */ 
  sandbox?: 'empty' | 'local' | SandboxFactory | BashFactory;
}

Internally, the init routine resolves the sandbox option in packages/sdk/src/client.ts (around line 225). For 'local', it falls back to the Node-specific implementation in packages/sdk/src/node/local-env.ts, which creates a SessionEnv that forwards all operations to the host’s fs and child_process APIs while maintaining the same interface used by remote sandboxes.

Practical CI/CD Scenarios

Using sandbox: 'local' is optimal for CI/CD workflows that require host filesystem interaction:

  • Reading source files – Direct fs.readFile calls access the repository checkout without copying files into an in-memory sandbox.
  • Running build tools – Commands like npm run build or pnpm install execute with the real $PATH and environment variables visible to the host shell.
  • Persisting generated artifacts – Files written to the runner’s filesystem are automatically available for subsequent workflow steps (artifact upload, caching).
  • Accessing CI secrets – Since the sandbox does not mask process.env, environment variables containing secrets remain accessible to tooling while staying out of the agent's explicit context.

Important: The local sandbox is not a security boundary. If you need to keep secrets off the host or prevent the LLM from accessing sensitive data, use a true remote sandbox connector (Daytona, E2B, Cloudflare Containers, etc.) and expose only necessary tools via Flue’s tool framework.

GitHub Actions Workflow Example

The following workflow demonstrates running a Flue agent with local sandbox access in a GitHub Actions environment:


# .github/workflows/test.yml

name: Flue CI
on: [push]

jobs:
  run-agent:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build SDK (required before running agents)
        run: pnpm --filter @flue/sdk run build

      - name: Run a Flue agent with local sandbox
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          # Initialise a harness that points at the repository root.

          node packages/cli/bin/flue.mjs run hello \
            --target node \
            --id ci-run \
            --payload '{"name":"CI"}' \
            --sandbox local   # <-- NOTE: this flag is a shortcut for init({ sandbox:'local' })

Agent Implementation Examples

Inside the agent code, use the ctx.fs surface to interact with the host filesystem. The following TypeScript agent (agents/hello.ts) demonstrates reading a source file and executing a build command:

import { init } from '@flue/sdk/node';

export default async function () {
  const harness = await init({ sandbox: 'local' }); // <-- explicit, same as CLI flag

  // Read a source file from the host repository
  const source = await harness.fs.readFile('src/index.ts');
  console.log('First line of source:', source.split('\n')[0]);

  // Run a build step – the command executes in the CI container
  const { stdout } = await harness.exec('npm run build', { timeout: 60 });
  console.log('Build output:', stdout);
}

To persist results back to the CI workspace for later steps:

await harness.fs.writeFile('test-results.txt', testReport);

Summary

  • The local sandbox (sandbox: 'local') delegates filesystem operations directly to Node.js fs/promises APIs via createCwdSessionEnv in packages/sdk/src/sandbox.ts.
  • Configuration occurs through the AgentInit interface in packages/sdk/src/types.ts, with resolution handled in packages/sdk/src/client.ts.
  • Shell commands execute with full host environment access and no path remapping, matching standard CI script behavior.
  • Ideal for CI/CD scenarios requiring direct repository access, build tool execution, and artifact persistence without virtualization overhead.
  • Not a security boundary; use remote sandbox connectors for sensitive secret isolation according to docs/sandbox-connector-spec.md.

Frequently Asked Questions

What is the difference between sandbox: 'empty' and sandbox: 'local' in Flue?

The 'empty' option creates an in-memory virtual filesystem completely isolated from the host, requiring files to be explicitly provided to the agent. The 'local' option delegates all filesystem operations to the host's Node.js implementation, providing direct access to the CI container's filesystem and environment.

How do I enable host filesystem access when running Flue agents in GitHub Actions?

Pass the --sandbox local flag to the Flue CLI command, which maps to init({ sandbox: 'local' }) in the SDK. This configuration delegates file operations to the host filesystem, allowing agents to read checked-out repository files and write artifacts that subsequent workflow steps can access.

Is the local sandbox secure for running untrusted code in CI/CD pipelines?

No. The local sandbox is explicitly not a security boundary—it provides direct access to the host filesystem and environment variables. For untrusted code or when handling sensitive secrets that must remain invisible to the LLM, use a remote sandbox connector (such as Daytona, E2B, or Cloudflare Containers) that provides proper isolation.

Which source files implement the local sandbox functionality?

The core implementation resides in packages/sdk/src/node/local-env.ts, which creates a SessionEnv forwarding operations to host APIs. The sandbox type resolution occurs in packages/sdk/src/client.ts (around line 225), with type definitions in packages/sdk/src/types.ts and the FlueHarness API exposed in packages/sdk/src/harness.ts.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →