* feat(sandbox): add BoxLite micro-VM sandbox provider (scaffold) Community SandboxProvider backed by BoxLite, a daemonless OCI-native micro-VM runtime. execute_command is wired end to end through a private asyncio loop that bridges BoxLite's async SDK to DeerFlow's sync Sandbox contract; the remaining Sandbox methods are stubbed pending approach review. Opt-in via sandbox.use (pip install boxlite); a packaged [boxlite] extra will follow. Existing providers are unchanged. Refs #3936, #3439, #3213 * feat(sandbox): implement full Sandbox contract for BoxLite backend Rename the community BoxLite integration to read as a compute backend rather than "a sandbox": module boxlite_sandbox -> boxlite, BoxliteSandboxProvider -> BoxliteProvider, BoxliteSandbox -> BoxliteBox. Implement the file surface DeerFlow's default tool path assumes -- read_file, write_file, update_file, download_file, list_dir, glob, grep -- as shell commands inside the box (cat/find/grep/chunked base64), reusing deerflow.sandbox.search and busybox-portable flags. Removes the reachable NotImplementedError regression once the provider is selected. download_file keeps the /mnt/user-data prefix + traversal guards; the provider materialises those virtual dirs on box start. Refs #3936, #3940 * fix(sandbox): resolve CI + review findings on BoxLite backend - provider: import DEFAULT_SKILLS_CONTAINER_PATH instead of the "/mnt/skills" literal (backend-unit-tests guard), and drop the redundant env-var re-resolution -- AppConfig.resolve_env_variables already resolves $VARS and raises on missing. - box: grep now passes the raw pattern to grep (-F/-E); it previously handed the re.escape'd pattern to grep -F, so a literal search of e.g. foo.bar looked for foo\.bar and never matched. - box: execute_command now calls _validate_extra_env(env), matching the Sandbox POSIX env-key contract that the local/e2b/aio sandboxes enforce. - add tests/test_boxlite_provider.py (CI-safe, no BoxLite): actionable ImportError on the lazy import, clean acquire-failure + shutdown, the traversal / download-prefix guards, and env-key rejection. Refs #3936, #3940
BoxLite backend
Runs each DeerFlow sandbox as a BoxLite micro-VM — a daemonless, OCI-native VM with its own kernel (libkrun/KVM on Linux, Hypervisor.framework on macOS). Motivated by the resource/cold-start pain with the default AIO Docker sandbox in #3439 and #3213; discussion in #3936.
Configuration
sandbox:
use: deerflow.community.boxlite:BoxliteProvider
image: python:3.12-slim # any OCI image, run unchanged (default: python:3.12-slim)
memory_mib: 1024 # per-box memory cap (optional)
cpus: 2 # per-box vCPUs (optional)
environment: # injected into every command
PYTHONUNBUFFERED: "1"
pip install boxlite # an optional `[boxlite]` extra + uv.lock update will follow once the approach lands
Host requirement: BoxLite boots micro-VMs, so a Linux host needs KVM — i.e. nested virtualization when DeerFlow runs inside a cloud VM. macOS uses Hypervisor.framework. This is the main deployment constraint to weigh vs. the container-based providers.
Design
DeerFlow's Sandbox contract is synchronous; BoxLite's SDK is async-native and
its box handles are event-loop-affine. The provider owns one private asyncio
loop on a daemon thread and marshals every coroutine onto it via
run_coroutine_threadsafe. This keeps all operations on the loop the box was
started on and is safe under DeerFlow's asyncio.to_thread worker pool — without
using BoxLite's greenlet sync facade, which refuses to run inside an async
context and is thread-affine.
| File | Role |
|---|---|
provider.py |
SandboxProvider lifecycle + the private-loop bridge |
box.py |
Sandbox adapter; execute_command + file ops |
Contract coverage
The full Sandbox surface is implemented. File operations run as shell commands
inside the box and reuse deerflow.sandbox.search, mirroring e2b_sandbox:
execute_command—sh -lc, with per-call env and timeout.read_file/write_file/update_file—catand chunkedbase64(binary-safe, no arg-size limit).download_file— 100 MB cap, restricted to the/mnt/user-dataprefix.list_dir/glob/grep—find/grepwith busybox-portable flags; results filtered/capped in Python.
The provider creates /mnt/user-data/{workspace,uploads,outputs} and
/mnt/skills on box start so those virtual paths resolve natively.
Out of scope for this pass (follow-ups): warm pooling, idle reaping, mount syncing, and remote/provisioner modes.
Status
Verified end-to-end against a live box (provider resolution → execute_command
→ file ops) on macOS/HVF. Linux/KVM validation and benchmarks vs. the AIO
sandbox are tracked in
#3936.