Tool System: Sandbox Pool
1. Behavior and Context
In the orchestration pipeline, the SandboxPool sits between the SandboxBuilder and the ScriptExecutor.
- Warm Fleet: At startup, it pre-spawns a configurable number of containers (pool_size) per sandbox and keeps them in an idle queue.
- Checkout/Return Cycle: Containers are "checked out" via an asynchronous context manager for a single agent turn and "returned" once execution is complete.
- Backpressure Logic: It uses a Semaphore capped at pool_size + max_overflow. If all pre-warmed containers are busy, the pool allows spawning new ones on-demand up to the overflow limit. Once the overflow is reached, subsequent callers block until a container is returned, protecting the Docker daemon from OOM (Out of Memory) crashes.
- Hygiene: Every container has its /workspace (a tmpfs RAM-disk) wiped clean upon return, ensuring zero data leakage between different user sessions or tasks.
- Recycling: To prevent long-term memory leaks or filesystem bloat, containers are retired and replaced after a maximum number of uses (MAX_CONTAINER_USES = 50).
2. Purpose
- Latency Elimination: Reducing tool-call overhead from seconds to milliseconds.
- Concurrency Control: Safely managing hundreds of concurrent requests via a bounded resource pool.
- DooD Compatibility: Automating complex host-path translations so that bind-mounts work correctly even when the agent controller itself is running inside a container.
- Secure Communication: Implementing a custom multiplexing reader to handle Docker's low-level framing protocol for reliable stdout/stdin communication.
3. High-Level API & Example
The SandboxPool is initialized with the ToolRegistry and the SandboxBuilder.
Example: Initializing and Using the Pool
import asyncio
from jazzmine.core.tools import SandboxPool, SandboxBuilder, registry, SandboxConfig
async def main():
# 1. Setup Infrastructure
builder = SandboxBuilder(registry)
# 2. Initialize Pool with secrets and DooD path mapping
pool = SandboxPool(
registry=registry,
builder=builder,
secrets={"DB_PASSWORD": "secure_value"},
max_overflow=10,
host_path_map={"/app/data": "/srv/physical_host/data"}
)
# 3. Start the pool (pre-warms containers)
await pool.startup()
# 4. Use a container via context manager
async with pool.checkout("default") as container:
print(f"Acquired container: {container.container_id[:12]}")
# Scripts are sent via container.stdin_pipe
# Results are read via container.stdout_pipe
# 5. Shutdown
await pool.shutdown()
if __name__ == "__main__":
asyncio.run(main())5. Detailed Functionality
Class: SandboxPool
startup(sandbox_names)
Builds required images and pre-starts the fleet of containers. It acquires semaphore slots for every pre-warmed container to ensure the capacity counter is accurate from the start.
checkout(sandbox_name)
Functionality: Returns an async context manager for safe container acquisition.
- Fast Path: If the idle queue for the sandbox has containers, one is returned immediately.
- Overflow Path: If the queue is empty but the overflow budget is available, a new container is spawned.
- Blocked Path: If the semaphore is fully depleted, the caller blocks (asynchronously) until capacity is released.
_spawn(...) [Private]
Functionality: The physical creation of a Docker container.
- Secret Injection: Resolves secret values from the secrets map or host os.environ.
- Network Plumbing: Connects the container to the isolated bridge and configures HTTP_PROXY settings pointing to the sidecar proxy.
- DooD Path Translation: Maps agent-local filesystem paths to real host paths using the host_path_map so the Docker daemon can perform bind-mounts.
- Hardening: Enforces read_only=True, drops all Linux capabilities, and prevents privilege escalation.
_translate_host_path(path) [Private]
Implements prefix-translation for Docker-out-of-Docker. It sorts mapping entries by length descending so that the "longest match" always wins (e.g., /data/db matches before /data).
Class: DockerLineReader
Functionality: Solves the Docker multiplexing problem.
- Docker sockets use an 8-byte framing protocol: [1B stream_type][3B padding][4B length].
- This class correctly parses these headers and maintains an internal buffer across frame boundaries, ensuring that readline() always returns complete, clean JSON strings from the container's stdout.
Class: ManagedContainer
A record representing a live container instance.
- stdin_pipe: An asyncio.StreamWriter used to transmit JSON requests to the containerized harness.
- stdout_pipe: A DockerLineReader used to receive JSON events.
- use_count: Tracks how many times this specific instance has been used to trigger recycling.
6. Error Handling
- RuntimeError (Spawn): Raised if a container fails to start (e.g., due to an invalid Docker configuration or failed health check). The pool automatically releases the semaphore slot to prevent a "deadlock" on capacity.
- ValueError (Pool Missing): Raised if checkout is called for a sandbox that wasn't included in the startup() list.
- asyncio.TimeoutError: Raised during _spawn if the container's internal harness does not emit a {"type": "ready"} event within 30 seconds.
- Crashes: If a container crashes or is killed by the OOM killer during a task, the stdout_pipe will raise an IncompleteReadError. The ScriptExecutor catches this, and the pool ensures the container is retired rather than returned to the queue.
7. Remarks
- Resource Management: Every spawned container is tracked in an internal list under a mutex lock. The shutdown() method is guaranteed to kill and remove every container, including those currently checked out.
- Thread Safety: The pool is designed for asyncio. It offloads blocking Docker SDK calls (which are synchronous) to a thread pool via loop.run_in_executor to keep the event loop free.
- Hygienic Scratch Space: The /workspace directory is a tmpfs (RAM-disk). Wiping it on return is an O(1) operation that consumes zero disk I/O, making container reuse extremely efficient.