Repository navigation
Closed as not planned
Closed as not planned
Copy link
Labels
needs review[Status] The PR/issue is awaiting review from the maintainer[Status] The PR/issue is awaiting review from the maintainertools[Component] This issue is related to tools[Component] This issue is related to tools
Description
Add exec-sandbox code executor (self-hosted QEMU microVMs)
Summary
Proposing exec-sandbox as a new BaseCodeExecutor backend. It runs code in ephemeral QEMU microVMs with hardware-level isolation (KVM on Linux, HVF on macOS) — self-hosted, no cloud account needed.
Why
- Self-hosted — ContainerCodeExecutor needs Docker, GkeCodeExecutor needs GKE, VertexAiCodeExecutor needs Vertex. exec-sandbox runs on bare metal with just QEMU. Data never leaves the machine — relevant for customer data privacy, regulated environments, and cheaper at scale.
- macOS + Linux — HVF on macOS, KVM on Linux. Same codebase for dev and prod.
- Fast — ~1-2ms from warm pool, ~200ms from memory snapshots.
- Stronger isolation than containers — Hardware-level VM boundary vs Docker process isolation.
- Apache-2.0
How it maps
exec-sandbox is a Python library with an async API. A BaseCodeExecutor subclass bridges async → sync, following the same pattern as ContainerCodeExecutor:
import asyncio
from exec_sandbox import Scheduler
class ExecSandboxCodeExecutor(BaseCodeExecutor):
stateful: bool = True
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._loop = asyncio.new_event_loop()
self._scheduler = self._loop.run_until_complete(Scheduler().__aenter__())
self._sessions: dict[str, Session] = {}
def execute_code(self, invocation_context, code_execution_input):
# Reuse session for stateful execution
eid = code_execution_input.execution_id
if eid and eid in self._sessions:
session = self._sessions[eid]
else:
session = self._loop.run_until_complete(
self._scheduler.session(language="python")
)
if eid:
self._sessions[eid] = session
result = self._loop.run_until_complete(session.exec(code_execution_input.code))
return CodeExecutionResult(
stdout=result.stdout,
stderr=result.stderr,
)Usage:
agent = LlmAgent(
code_executor=ExecSandboxCodeExecutor(),
...
)Open questions
- Async/sync bridge — exec-sandbox is fully async. The sketch uses a dedicated event loop. A background-thread loop would be safer if called from an existing async context.
- File I/O —
CodeExecutionInput.input_filesandCodeExecutionResult.output_filescould use exec-sandbox's nativewrite_file/read_fileAPIs instead of writing to a shared volume. - Shipping — External package (
exec-sandbox[adk]extra) vs upstream incode_executors/following the GkeCodeExecutor pattern from PR feat(code_executors): Add GkeCodeExecutor for sandboxed code execution on GKE #1629.
Links
Happy to submit a PR if there's interest.
Metadata
Metadata
Assignees
Labels
needs review[Status] The PR/issue is awaiting review from the maintainer[Status] The PR/issue is awaiting review from the maintainertools[Component] This issue is related to tools[Component] This issue is related to tools
Activity
klateefa commentedon Mar 2, 2026
@clemlesne Thanks for reporting!
Your request is being reviewed, and I’ll update you if we need anything else. Thanks for your patience.
DeanChensj commentedon Oct 14, 2026
Thank you for sharing
exec-sandboxand for the detailed proposal!To keep the core
google-adkpackage dependencies and maintenance surface focused, we only maintain first-party and standard local/container executors directly in the core repository (UnsafeLocalCodeExecutor,ContainerCodeExecutor,VertexAiCodeExecutor,AgentEngineSandboxCodeExecutor).BaseCodeExecutoris designed as an extensible public interface so that specialized sandbox backends likeexec-sandboxcan be published and maintained as standalone community packages.We're closing this issue for now, but if you run into any limitations or friction in the
BaseCodeExecutorinterface while integratingexec-sandbox, please feel free to open a new issue!