Skip to content

Sandboxing

EctoSpace edited this page Nov 13, 2026 · 1 revision

Sandboxing

Every command an agent runs passes four checks first (see Concepts and Architecture). The sandbox is the last line: it limits what an approved command can actually touch. The full reference is docs/reference/sandboxing.md.

Fail-closed by default

If a sandbox cannot be applied (no Landlock in the kernel, a seccomp filter rejected, sandbox_init() failing on macOS, a Firecracker guest that produces no output), the action is aborted, not run unprotected on the host. ECTO_SANDBOX_PERMISSIVE=true switches to a logged warning and advisory checks only. Leave it unset outside development.

What each platform gives you

Platform Mechanism Filesystem Process Resource limits
Linux Landlock + seccomp-BPF + rlimits (build feature sandbox, on by default) Landlock, workspace read-only seccomp syscall allowlist rlimits: CPU, address space, file descriptors
macOS Seatbelt (sandbox_init), applied to each child process Read and write in the workspace only process-exec allowed; commands limited by the tripwire and executable allowlist Not enforced
Windows Job Objects with UI restrictions No restriction beyond NTFS permissions Cleanup guaranteed, no syscall filtering Partial
Docker / Podman A container per command Read-only root Container, --user nobody cgroups
Firecracker A microVM per command Guest filesystem KVM microVM vCPU and memory

Linux

Needs kernel 5.13 or newer with Landlock enabled. A kernel without Landlock is a sandbox failure, so the command is aborted. The rlimits cap a command at 30 seconds of CPU, 512 MiB of address space and 64 file descriptors. A seccomp filter that the kernel rejects stops serve; it does not continue unsandboxed.

macOS

The Seatbelt profile allows read access to system libraries, read and write in the workspace, stat (not listing) of the workspace's parent folders, and denies network and signals for child processes. Known limits:

  • /usr/bin/git and other Xcode command-line shims do not run under the profile. Use a Homebrew git (/opt/homebrew/bin) or run without the sandbox.
  • ls .. from the workspace is refused; ls -la in the workspace works.
  • No resource limits. Only the child process loses network access, not IronVise itself.
  • Apple has deprecated the public sandbox_init header; the enforcement is still active.

For production on macOS, use the Docker tier or run IronVise in a Linux container.

Windows

Job Objects guarantee child processes are cleaned up (KILL_ON_JOB_CLOSE) and block UI escalation such as clipboard access and display changes. They do not restrict file access or filter system calls. For production on Windows, use the Docker tier.

Docker / Podman tier

Works on every platform with Docker or Podman installed. Each run_command runs in a fresh container with --network=none, --read-only, --security-opt no-new-privileges, --user nobody, and --memory / --cpus limits.

export ECTO_DOCKER_IMAGE=alpine:3.19
export ECTO_DOCKER_RUNTIME=auto      # auto, docker or podman
export ECTO_DOCKER_MEMORY=256m
export ECTO_DOCKER_CPUS=1
export ECTO_DOCKER_TIMEOUT=30

This is the simplest way to get strong isolation with no guest image to build.

Firecracker tier

An optional isolation tier for Linux hosts with KVM: each run_command runs in an ephemeral microVM that is destroyed afterwards. It gives hardware-level isolation and is the strongest tier.

Requirements

  • Linux with KVM, and a build with the feature: cargo build --release -p ectoledger --features sandbox,sandbox-firecracker
  • Firecracker 1.5 or newer
  • A guest kernel (vmlinux) and a root filesystem that implements the IronVise guest protocol (below)
export ECTO_FC_BINARY=/usr/local/bin/firecracker   # setting this opts in
export ECTO_FC_KERNEL=/opt/ectoledger/vmlinux
export ECTO_FC_ROOTFS=/opt/ectoledger/rootfs.ext4
export ECTO_FC_VCPUS=1
export ECTO_FC_MEM_MIB=128
export ECTO_FC_TIMEOUT_SECS=30
ironvise audit "Your goal"

Getting a working guest image

scripts/provision-firecracker.sh --rootfs busybox

builds a minimal busybox root filesystem whose init implements the protocol. Without --rootfs busybox the script first tries the Firecracker project's official Ubuntu CI rootfs, which does not implement the protocol; it falls back to busybox only if that download fails. If an official rootfs is already installed, delete it and re-run with --rootfs busybox.

scripts/setup-firecracker.sh installs the Firecracker project's hello kernel and rootfs. That image is a plumbing test only (KVM, binary, kernel and block devices): it contains no IronVise guest agent and executes no intent.

A microVM with a rootfs that does not implement the protocol produces no output, and the action is aborted (fail-closed).

The guest image contract

Device Contents
/dev/vda Your rootfs, the root device
/dev/vdb The intent as a raw JSON image (not a filesystem: read the device directly)
/dev/ttyS0 Serial console; its output becomes the command's output on the host

The guest init must:

  1. Read the raw bytes of /dev/vdb and strip trailing NUL padding.
  2. Parse the JSON, handle action == "run_command" and take params.command. Treat any other action as unsupported.
  3. Run the command inside the guest.
  4. Write its stdout and stderr to /dev/ttyS0.
  5. Shut down (for example reboot -f). The host applies ECTO_FC_TIMEOUT_SECS if the guest hangs.

An illustrative shell init and image-building notes are in the guest image contract. Use a real JSON parser in anything you deploy.

Recommended setups

Where Setup
Linux production Native sandbox (default build); add Firecracker or Docker for untrusted workloads
macOS production Docker tier, or IronVise in a Linux container
Windows production Docker tier
Development on any OS ECTO_DOCKER_IMAGE=alpine:3.19

Guard enclaves

Separate from command sandboxing, three optional enclave runtimes can hold the guard model's inference inside a confidentiality boundary: Level 1 software-hardened (--features enclave), Level 2 Apple Hypervisor on Apple Silicon (--features sandbox-apple-enclave), and Level 3 remote hardware enclave with AWS Nitro attestation (--features enclave-remote). In 0.8 these runtimes are built and tested but not wired into the agent loop: no prompt is routed through an enclave. Details are in the sandboxing reference.

Clone this wiki locally