Sandbox
Core reference

Sandbox Pool

The SandboxPool is the high-concurrency fleet management engine for the jazzmine framework. Its primary responsibility is to maintain a steady supply of "warm" (pre-started) Docker containers for every configured sandbox. By eliminating the 1–3 second "cold start" delay associated with creating Docker containers on-demand, the pool ensures that tool execution is near-instantaneous. It also serves as a critical backpressure mechanism, preventing resource exhaustion on the host machine by bounding the total number of concurrent execution environments.

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

python
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.