kosa8 / Guides

Guide · Updated 2026-10-31

How do I sandbox LangChain and LangGraph code execution?

PythonREPLTool from langchain-experimental runs the model's Python with exec() in a process on your own machine, so the code can do whatever you can. Replace it with a small tool that sends the code to a kosa8 microVM with no network, and in LangGraph add an interrupt() so a person approves each run. kosa8 has no separately documented LangChain Python integration; this pattern uses documented kosa8 CLI commands and LangChain's own tool API.

01Steps

  1. Install kosa8. On an Apple silicon Mac or x86-64 Linux:

  2. Create a sandbox with no network. This is a microVM with its own kernel. --egress deny is enforced on the host, so nothing leaves it, DNS included. Use --egress-allow pypi.org --egress-allow files.pythonhosted.org instead if the code must install packages.

  3. Pull an image and take a clean checkpoint. The checkpoint captures the sandbox's memory, processes and disk, so every task can start from this state.

    terminal
    kosa8 sandbox pull code-runner python:3.12-slim
    kosa8 checkpoint code-runner --label clean
  4. Define the tool. Each call starts a container from the image inside the sandbox's own engine and returns its exit code and output. Generated code never runs in your Python process.

    sandbox_tool.py
    import subprocess
     
    SANDBOX = "code-runner"          # created with: kosa8 sandbox create code-runner --egress deny
    IMAGE = "python:3.12-slim"
     
    def run_in_kosa8(code: str, timeout: int = 120) -> str:
        """Run Python in a container inside the kosa8 microVM and return its output."""
        try:
            p = subprocess.run(
                ["kosa8", "sandbox", "run", SANDBOX, IMAGE, "python", "-c", code],
                capture_output=True, text=True, timeout=timeout,
            )
        except subprocess.TimeoutExpired:
            return f"error: no result after {timeout}s"
        return f"exit code {p.returncode}\nstdout:\n{p.stdout}\nstderr:\n{p.stderr}"
     
    from langchain_core.tools import tool
     
    @tool
    def run_python(code: str) -> str:
        """Run Python code in an isolated sandbox with no network. Print results."""
        return run_in_kosa8(code)
  5. Add an approval step in LangGraph (optional). interrupt() pauses the graph and hands the code to a person. The graph needs a checkpointer and a thread_id; resume with Command(resume="approve").

    approval.py
    # add to sandbox_tool.py
    from langgraph.types import interrupt
     
    @tool
    def run_python_approved(code: str) -> str:
        """Run Python code in the sandbox after a person approves it."""
        decision = interrupt({"question": "Run this code in the sandbox?", "code": code})
        if decision != "approve":
            return "The user rejected this code."
        return run_in_kosa8(code)
  6. Rewind between tasks, and check the record. Rewind returns the whole machine to the clean checkpoint, which also clears anything a timed-out run left behind. The audit log records every action, and kosa8 audit verify checks its hash chain.

    terminal
    kosa8 rewind code-runner --to clean --yes
    kosa8 audit ls
    kosa8 audit verify

02Is LangChain's Python REPL tool safe?

Not for code you have not read. PythonREPL runs the code with Python's exec() on your machine and logs the warning "Python REPL can execute arbitrary code. Use with caution." The langchain-experimental README says parts of the package may be dangerous unless deployed in a sandboxed environment, and the repository now says the package is being sunset.

03Why is PythonREPLTool opt-in only?

Because agents built on it can be steered into running anything. LangChain's agents that rely on a Python REPL, such as the pandas DataFrame agent, refuse to start unless you pass allow_dangerous_code=True, and their docstring says failure to sandbox them can lead to arbitrary code execution vulnerabilities, data breaches or data loss.

Setting that flag is a statement that you have provided the sandbox. The tool above is one way to provide it: the code runs behind a VM boundary, with a host-enforced network policy and a machine you can rewind.

04How does the LangGraph approval step work?

interrupt() pauses the graph and returns its payload to your application. LangGraph's docs require a checkpointer and a thread ID in the config, and you resume with Command(resume=value) on the same thread; that value becomes the return value of interrupt().

When the graph resumes, the node or tool starts again from the top, so keep work before interrupt() free of side effects, as in the example. Use InMemorySaver for experiments and a durable checkpointer in production.

05Does kosa8 work with LangChain.js?

Through MCP, yes. kosa8's docs test @langchain/mcp-adapters 2.0.1 against kosa8 mcp serve, which gives a LangChain.js agent kosa8's tools (named kosa8_sandbox_list, kosa8_container_exec and so on) to create and use sandboxes itself.

06Other options

  • LangChain's code interpreter integrations. LangChain's tools page lists hosted interpreters such as Amazon Bedrock AgentCore Code Interpreter and Azure Container Apps dynamic sessions.
  • langchain-sandbox. Runs Python in Pyodide (WebAssembly) under Deno. Its README now says it is no longer maintained and not recommended for production.
  • E2B, Daytona or Modal. Hosted sandboxes called over an API from a custom tool, a fit when execution should happen in the cloud.
  • A Docker container. The same custom-tool pattern with docker run --network none; containers share the host kernel on Linux.
  • gVisor. On Linux, a user-space kernel under each container via the runsc runtime.

07Sources

Updated 2026-10-31