中文 | English
📖 中文文档 | 📖 English Documentation
⭐ If you like this project, please click the "Star" button in the upper right corner to support us. Your support is our motivation to move forward!
ms-enclave is a modular and stable sandbox runtime environment that provides a secure isolated execution environment for applications. It implements strong isolation through Docker containers, equipped with local/HTTP managers and an extensible tool system, helping you execute code safely and efficiently in a controlled environment.
- 🔒 Secure Isolation: Complete isolation and resource limits based on Docker
- 🧩 Modular: Both sandboxes and tools are extensible (registered factory)
- ⚡ Stable Performance: Clean implementation, fast startup, with lifecycle management
- 🌐 Remote Management: Built-in FastAPI service, supports HTTP management
- 🔧 Tool System: Standardized tools enabled by sandbox type (OpenAI-style schema)
- Python >= 3.10
- Operating System: Linux, macOS, or Windows with Docker support
- Docker daemon available on local machine (Notebook sandbox requires port 8888 open)
pip install ms-enclave
# If Docker support is needed, install extra dependencies
pip install 'ms-enclave[docker]'git clone https://github.com/modelscope/ms-enclave.git
cd ms-enclave
pip install -e .# If Docker support is needed, install extra dependencies
pip install -e '.[docker]'Tools need to be explicitly enabled in the configured tools_config, otherwise they won't be registered.
importasynciofromms_enclave.sandbox.boxesimportSandboxFactoryfromms_enclave.sandbox.modelimportDockerSandboxConfig, SandboxTypeasyncdefmain():
config=DockerSandboxConfig(
image='python:3.11-slim',
memory_limit='512m',
tools_config={
'python_executor': {},
'file_operation': {},
'shell_executor': {}
}
)
asyncwithSandboxFactory.create_sandbox(SandboxType.DOCKER, config) assandbox:
# 1) Write fileawaitsandbox.execute_tool('file_operation', {
'operation': 'write', 'file_path': '/sandbox/hello.txt', 'content': 'hi from enclave'
})
# 2) Execute Python coderesult=awaitsandbox.execute_tool('python_executor', {
'code': "print('Hello from sandbox!')\nprint(open('/sandbox/hello.txt').read())"
})
print(result.output)
asyncio.run(main())Expose sandbox tools to the Agent in OpenAI Tools format, allowing the model to trigger tools and execute them securely in the sandbox.
importasyncio, os, jsonfromopenaiimportOpenAIfromms_enclave.sandbox.managerimportSandboxManagerFactoryfromms_enclave.sandbox.modelimportDockerSandboxConfig, SandboxTypeasyncdefdemo():
client=OpenAI(
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key=os.getenv("DASHSCOPE_API_KEY")
)
asyncwithSandboxManagerFactory.create_manager() asm:
cfg=DockerSandboxConfig(image="python:3.11-slim", tools_config={"python_executor": {}, "shell_executor": {}})
sid=awaitm.create_sandbox(SandboxType.DOCKER, cfg)
tools=list((awaitm.get_sandbox_tools(sid)).values())
messages= [{"role": "user", "content": "Print 'hello' in Python, then list /sandbox via shell."}]
rsp=client.chat.completions.create(model="qwen-plus", messages=messages, tools=tools, tool_choice="auto")
msg=rsp.choices[0].messagemessages.append(msg.model_dump())
ifgetattr(msg, "tool_calls", None):
fortcinmsg.tool_calls:
name=tc.function.nameargs=json.loads(tc.function.argumentsor"{}")
result=awaitm.execute_tool(sid, name, args)
messages.append({"role": "tool", "content": result.model_dump_json(), "tool_call_id": tc.id, "name": name})
final=client.chat.completions.create(model="qwen-plus", messages=messages)
print(final.choices[0].message.contentor"")
else:
print(msg.contentor"")
asyncio.run(demo())Notes:
- Use
get_sandbox_tools(sandbox_id)to retrieve tool schemas (OpenAI-compatible) - Pass
tools=...to the model, handle returnedtool_callsand execute them in the sandbox - Call the model again to generate the final answer
- Direct use of SandboxFactory: Create/destroy sandboxes within a single process, most lightweight; suitable for scripts or one-time tasks
- Using LocalSandboxManager: Uniformly orchestrate lifecycle/cleanup of multiple sandboxes on local machine; suitable for service-oriented, multi-task parallel scenarios
- Using HttpSandboxManager: Manage sandboxes uniformly through remote HTTP service; suitable for cross-machine/distributed or stronger isolation deployments
When to use:
- You want a single entry point that chooses Local or HTTP manager automatically.
- You prefer central registration and discovery of available manager types.
Key points:
- If manager_type is provided, it is used directly.
- If base_url is provided (in config or kwargs), HTTP manager is created.
- Otherwise, Local manager is created by default.
Example: implicit selection by base_url
importasynciofromms_enclave.sandbox.managerimportSandboxManagerFactoryasyncdefmain():
asyncwithSandboxManagerFactory.create_manager(base_url='http://127.0.0.1:8000') asm:
# Use exactly like HttpSandboxManager# e.g., create a DOCKER sandbox and execute a tool# ... your code ...passasyncio.run(main())Example: explicit selection + custom config
importasynciofromms_enclave.sandbox.managerimportSandboxManagerFactoryfromms_enclave.sandbox.modelimportSandboxManagerConfig, SandboxManagerTypeasyncdefmain():
cfg=SandboxManagerConfig(cleanup_interval=600)
asyncwithSandboxManagerFactory.create_manager(
manager_type=SandboxManagerType.LOCAL, config=cfg
) asm:
# Use exactly like LocalSandboxManager# ... your code ...passasyncio.run(main())Discover registered manager types:
fromms_enclave.sandbox.managerimportSandboxManagerFactoryprint(SandboxManagerFactory.get_registered_types())Use Cases:
- Temporarily run a piece of code in scripts or microservices
- Fine-grained control over sandbox lifecycle (cleanup on context exit)
Example (Docker sandbox + Python execution):
importasynciofromms_enclave.sandbox.boxesimportSandboxFactoryfromms_enclave.sandbox.modelimportDockerSandboxConfig, SandboxTypeasyncdefmain():
cfg=DockerSandboxConfig(
tools_config={'python_executor': {}}
)
asyncwithSandboxFactory.create_sandbox(SandboxType.DOCKER, cfg) assb:
r=awaitsb.execute_tool('python_executor', {
'code': 'import platform; print(platform.python_version())'
})
print(r.output)
asyncio.run(main())Use Cases:
- Need to create/manage multiple sandboxes within the same process (create, query, stop, periodic cleanup)
- Want unified status view, statistics, and health checks
Example:
importasynciofromms_enclave.sandbox.managerimportLocalSandboxManagerfromms_enclave.sandbox.modelimportDockerSandboxConfig, SandboxTypeasyncdefmain():
asyncwithLocalSandboxManager() asmanager:
cfg=DockerSandboxConfig(tools_config={'shell_executor': {}})
sandbox_id=awaitmanager.create_sandbox(SandboxType.DOCKER, cfg)
# Execute commandres=awaitmanager.execute_tool(sandbox_id, 'shell_executor', {'command': 'echo hello'})
print(res.output.strip()) # hello# View listinfos=awaitmanager.list_sandboxes()
print([i.idforiininfos])
# Stop and deleteawaitmanager.stop_sandbox(sandbox_id)
awaitmanager.delete_sandbox(sandbox_id)
asyncio.run(main())Use Cases:
- Run sandbox service on a separate host/container, invoke remotely via HTTP
- Multiple applications share a secure controlled sandbox cluster
Start the service first (choose one):
# Method A: Command line
ms-enclave server --host 0.0.0.0 --port 8000
# Method B: Python startup
python -c "from ms_enclave.sandbox import create_server; create_server().run(host='0.0.0.0', port=8000)"Client example:
importasynciofromms_enclave.sandbox.managerimportHttpSandboxManagerfromms_enclave.sandbox.modelimportDockerSandboxConfig, SandboxTypeasyncdefmain():
asyncwithHttpSandboxManager(base_url='http://127.0.0.1:8000') asm:
cfg=DockerSandboxConfig(tools_config={'python_executor': {}})
sid=awaitm.create_sandbox(SandboxType.DOCKER, cfg)
r=awaitm.execute_tool(sid, 'python_executor', {'code': 'print("Hello remote")'})
print(r.output)
awaitm.delete_sandbox(sid)
asyncio.run(main())Why:
- Amortize container startup by keeping a fixed-size pool of ready sandboxes.
- Each execution borrows a sandbox and returns it; requests queue FIFO when all are busy.
Local pool example:
importasynciofromms_enclave.sandbox.managerimportLocalSandboxManagerfromms_enclave.sandbox.modelimportDockerSandboxConfig, SandboxTypeasyncdefmain():
asyncwithLocalSandboxManager() asm:
cfg=DockerSandboxConfig(
image='python:3.11-slim',
tools_config={'python_executor': {}}
)
# Create a pool of 2 pre-warmed sandboxesawaitm.initialize_pool(pool_size=2, sandbox_type=SandboxType.DOCKER, config=cfg)
# Execute multiple tasks; sandboxes are reused and queued FIFO when busytasks= [
m.execute_tool_in_pool('python_executor', {'code': f'print("task {i}")', 'timeout': 30})
foriinrange(5)
]
results=awaitasyncio.gather(*tasks)
print([r.output.strip() forrinresults])
# Pool statsstats=awaitm.get_stats()
print('pool_size =', stats['pool_size'])
asyncio.run(main())HTTP pool example:
importasynciofromms_enclave.sandbox.managerimportHttpSandboxManagerfromms_enclave.sandbox.modelimportDockerSandboxConfig, SandboxTypeasyncdefmain():
asyncwithHttpSandboxManager(base_url='http://127.0.0.1:8000') asm:
cfg=DockerSandboxConfig(image='python:3.11-slim', tools_config={'python_executor': {}})
awaitm.initialize_pool(pool_size=2, sandbox_type=SandboxType.DOCKER, config=cfg)
r=awaitm.execute_tool_in_pool('python_executor', {'code': 'print("hello from pool")', 'timeout': 30})
print(r.output)
asyncio.run(main())Notes:
- Waiting timeout:
await m.execute_tool_in_pool(..., timeout=1.0)raisesTimeoutErrorif no sandbox is available in time. - FIFO behavior: pool borrows/returns in FIFO order under load.
- Errors: even if a tool execution fails, the sandbox is returned to the pool.
Current built-in sandbox types:
DOCKER (General container execution)
- Supported tools:
- python_executor (Execute Python code)
- shell_executor (Execute Shell commands)
- file_operation (Read/write/delete/list files)
- multi_code_executor (Multi-language code execution, supports python, cpp, csharp, go, java, nodejs, ts, rust, php, bash, pytest, jest, go_test, lua, r, perl, d_ut, ruby, scala, julia, kotlin_script, verilog, lean, swift, racket) Requires specifying image
volcengine/sandbox-fusion:server-20250609
- Features: Configurable memory/CPU limits, volume mounts, network toggle, privileged mode, port mapping
- Supported tools:
DOCKER_NOTEBOOK (Jupyter Kernel Gateway environment)
- Supported tools:
- notebook_executor (Execute code through Jupyter kernel, supports saving code context)
- Note: This type only loads notebook_executor; other DOCKER-specific tools won't be enabled in this sandbox
- Dependencies: Requires port 8888 exposed, network enabled
- Supported tools:
Tool loading rules:
- Tools are only initialized and available when explicitly declared in
tools_config - Tools validate
required_sandbox_types, automatically ignored if mismatched
Example:
DockerSandboxConfig(tools_config={'python_executor': {}, 'shell_executor': {}, 'file_operation': {}})
DockerNotebookConfig(tools_config={'notebook_executor': {}})image: Docker image name (e.g.,python:3.11-slimorjupyter-kernel-gateway)memory_limit: Memory limit (e.g.,512m/1g)cpu_limit: CPU limit (float, >0)volumes: Volume mounts, formatted as{host_path: {"bind": "/container/path", "mode": "rw"}}ports: Port mapping, formatted as{ "8888/tcp": ("127.0.0.1", 8888) }network_enabled: Whether to enable network (Notebook sandbox requires True)remove_on_exit: Whether to delete container on exit (default True)
Manager Config (SandboxManagerConfig):
base_url: If set, HttpSandboxManager is selected automaticallycleanup_interval: Background cleanup interval in seconds (local manager)
Example of Installing Additional Dependencies in Sandbox
asyncwithSandboxFactory.create_sandbox(SandboxType.DOCKER, config) assandbox:
# 1) Write a filerequirements_file='/sandbox/requirements.txt'awaitsandbox.execute_tool('file_operation', {
'operation': 'write', 'file_path': f'{requirements_file}', 'content': 'numpy\npandas\nmodelscope\n'
})
# 2) Execute Python coderesult=awaitsandbox.execute_tool('python_executor', {
'code': f"print('Hello from sandbox!')\nprint(open(f'{requirements_file}').read())"
})
print(result.output)
# 3) Execute CLIresult_cli=awaitsandbox.execute_command(f'pip install -r {requirements_file}')
print(result_cli.stdout, flush=True)Example of Reading/Writing Host Files in Sandbox
asyncwithLocalSandboxManager() asmanager:
# Create sandboxconfig=DockerSandboxConfig(
# image='python-sandbox',image='python:3.11-slim',
tools_config={'python_executor': {}, 'file_operation': {}},
volumes={'~/Code/ms-enclave/output': {'bind': '/sandbox/data', 'mode': 'rw'}}
)
sandbox_id=awaitmanager.create_sandbox(SandboxType.DOCKER, config)
# Write fileresult=awaitmanager.execute_tool(
sandbox_id, 'file_operation', {'operation': 'write', 'file_path': '/sandbox/data/hello.txt', 'content': 'Hello, Sandbox!'}
)
print(result.model_dump())result=awaitsandbox.execute_tool('python_executor', {'code': 'print(1/0)'})
ifresult.error:
print('Error message:', result.error)
else:
print('Output:', result.output)# Clone repository
git clone https://github.com/modelscope/ms-enclave.git
cd ms-enclave
# Create virtual environment
conda create -n ms-enclave python=3.10 -y
conda activate ms-enclave
# Install dependencies
pip install -e ".[dev]"# Run tests
pytest
# Run examples (provided in repository)
python examples/sandbox_usage_examples.py
python examples/local_manager_example.py
python examples/server_manager_example.pyWe welcome contributions! Please check CONTRIBUTING.md for details.
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Develop and add tests
- Run tests locally:
pytest - Commit changes:
git commit -m 'Add amazing feature' - Push branch:
git push origin feature/amazing-feature - Submit Pull Request
This project is licensed under the Apache 2.0 License. See LICENSE for details.
