📦 ConTree SDK
SDK for ConTree: Sandboxes That Branch Like Git. ConTree is a container runtime purpose-built to support research on SWE agents, providing reproducible, versioned filesystem state — like Git for container execution, accessible from Python.
👉 See full feature list and use cases in the documentation →
📥 Get Started
Installation
Install the SDK from a PyPi:
pip install contree-sdk
Quick Start
🔀 Async Example
import asyncio
from contree_sdk import Contree
async def main():
# Get client
contree = Contree(token="fake-token")
# Use image by tag
image = await contree.images.use("ubuntu:latest")
# Run command
result = await image.run(shell='echo "Hello from Contree!"')
# Output result
print(result.stdout)
asyncio.run(main())
🔁 Sync Example
from contree_sdk import ContreeSync
def main():
# Get client
contree = ContreeSync(token="fake-token")
# Use image by tag
image = contree.images.use("ubuntu:latest")
# Run command
result = image.run(shell='echo "Hello from Contree!"').wait()
# Output result
print(result.stdout)
main()
Examples
Ready to explore more? Check out our comprehensive examples:
- Session Management - Working with persistent sessions and state management
- Image Operations - Advanced image pulling, versioning, and management
- Branching Workflows - Complex workflow patterns with image branching
Explore all examples in the examples/ directory
Development Setup
Prerequisites
- Python 3.10 - 3.13
- uv package manager
Env setup
git clone git@github.com:nebius/contree-sdk.git
cd contree-sdk
uv sync
Running Checks
Linting and formatting with Ruff:
uv run ruff check .
uv run ruff format .
Type checking with basedpyright:
uv run basedpyright
Running Tests
uv run pytest
Documentation Dev Server
make rtd-dev
Table of Contents
- Installation
- Quick Start
- Examples
- Development Setup
- Quick Start (Advanced)
- Core Concepts
- Advanced Usage
- License
🚀 Quick Start (Advanced)
🔀 Async Example
import asyncio
import stat
from pathlib import PurePosixPath
from contree_sdk import Contree
from contree_sdk.utils.models.file import UploadFileSpec
from contree_sdk.sdk.objects.image_fs import ImageFile
async def amain():
# create client
contree = Contree(token="fake-token")
# list images
images = await contree.images()
# use image by tag (no API call, resolved at execution time)
ubuntu_image = await contree.images.use("ubuntu:latest")
# pulling image from a remote registry
busybox_image = await contree.images.oci("docker://docker.io/busybox:latest")
# running command
result0 = await ubuntu_image.run(
command="/app.sh",
args=("arg1", "arg2"),
stdin="input",
env=dict(http_proxy="http://10.20.30.40:1234"),
files=[
UploadFileSpec(source="/local/files/app.sh", mode=stat.S_IXUSR),
UploadFileSpec(source="/local/files/data_ver1.csv", path=PurePosixPath("/data.csv")),
],
)
print(result0.stdout)
print(result0.stderr)
# running next command
result1 = await result0.run(shell="echo output.csv | grep something")
# getting files and directories by path
items = await result1.ls("files/path")
print(len(items))
# iterating through files and directories by path
for item in await result1.ls("~"):
print(item.name, item.is_dir)
if item.is_file:
# download file
assert isinstance(item, ImageFile)
await item.download("/local/files/downloaded/")
# using session
session = busybox_image.session()
await session.run(
command="/bin/app",
files=[UploadFileSpec(source="/local/files/app", path="bin/app", mode=stat.S_IXUSR)],
)
res = await session.run(command="/bin/cat", args=("result.txt",))
print(res.stdout)
# downloading file from session
await session.download("/tmp/log.jsonl", "/local/logs/session_1.log")
# or simply reading from file
content = await session.read("/tmp/log.jsonl")
print(content.decode())
asyncio.run(amain())
🔁 Sync Example
import stat
from contree_sdk import ContreeSync
from contree_sdk.utils.models.file import UploadFileSpec
from contree_sdk.sdk.objects.image_fs import ImageFileSync
def main():
# Create client
contree = ContreeSync(token="fake-token")
# list images
images = contree.images()
# Use image by tag (no API call, resolved at execution time)
ubuntu_image = contree.images.use("ubuntu:latest")
# Pulling image from a remote registry
busybox_image = contree.images.oci("docker://docker.io/busybox:latest")
# running command
result0 = ubuntu_image.run(
command="/app.sh",
args=("arg1", "arg2"),
stdin="input",
env=dict(http_proxy="http://10.20.30.40:1234"),
files=[
UploadFileSpec(source="/local/files/app.sh", mode=stat.S_IXUSR),
UploadFileSpec(source="/local/files/data_ver1.csv", path="/data.csv"),
],
).wait()
print(result0.stdout)
print(result0.stderr)
# running next command
result1 = result0.run(shell="echo output.csv | grep something").wait()
# getting files and directories by path
items = result1.ls("files/path")
print(len(items))
# iterating through files and directories by path
for item in result1.ls("~"):
print(item.name, item.is_dir)
if item.is_file:
assert isinstance(item, ImageFileSync)
# download file
item.download("/local/files/downloaded/")
# using session
session = busybox_image.session()
session.run(
command="/bin/app",
files=[UploadFileSpec(source="/local/files/app", path="/bin/app", mode=stat.S_IXUSR)],
).wait()
res = session.run(command="cat", args=("result.txt",)).wait()
print(res.stdout)
# downloading file from session
session.download("/tmp/log.jsonl", "/local/logs/session_1.log")
# or simply reading from file
content = session.read("/tmp/log.jsonl")
print(content.decode())
main()
🧠 Core Concepts
Sessions and Versioning
A session is essentially an image whose version automatically updates after each command execution. When you run commands, you're not modifying the original image - instead, each command creates a new version of the image with your changes applied.
import asyncio
from contree_sdk import Contree
async def amain():
contree = Contree(token="fake-token")
# Each command creates a new image version
image = await contree.images.use("busybox:latest") # busybox:latest
result1 = await image.run(shell="apt update") # some-uuid
result2 = await result1.run(shell="apt install python3") # another-uuid
# Sessions work the same way
session = image.session() # busybox:latest
await session.run(shell="touch /app/file1.txt") # some-uuid
await session.run(shell="echo 'hello' > /app/file1.txt") # another-uuid
asyncio.run(amain())
Subprocess-like interface
Any session can provide Subprocess-like interface
🔁 Sync examples
Running command
proc = session.popen(
["cat"],
text=True,
)
stdout, stderr = proc.communicate("a\nb\nc\n")
Shell example
import subprocess
proc = session.popen(
"echo hello && ls -la",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
returncode = proc.wait()
print(proc.stdout)
Stable image UUID
Basically one UUID refers to one state of FS, so in case if after running commands on the image, no FS changes are detected, UUID stays the same.
result0 = image.run("echo CHANGES > file.txt").wait()
result1 = result0.run("sleep 5").wait()
assert result1.uuid == result0.uuid
Async/sync clients and objects
Basically every object that is produced by async client is async-friendly and every object is produced by sync client is sync friendly. For example
import asyncio
from contree_sdk import Contree, ContreeSync
async def amain():
contree_async = Contree(token="fake-token")
# async client produces async-friendly images objects, so they can be used in async code
images = await contree_async.images()
await images[0].run(shell="some command")
asyncio.run(amain())
contree_sync = ContreeSync(token="fake-token")
# while sync client produces sync-friendly images objects, so they can be used in sync code
images = contree_sync.images()
images[0].run(shell="some command").wait()
Advanced Usage
Client configuration
You can create configuration object and use it later in client
from contree_sdk.auth import IAMAuth
from contree_sdk.config import ContreeConfig
from contree_sdk import Contree, ContreeSync
config = ContreeConfig(
auth=IAMAuth(token="my-token", base_url="https://contree.host.com"),
transport_timeout=10.0, # timeout for transport operations
)
client_async = Contree(config)
client = ContreeSync(config)
Authentication
The SDK resolves credentials in the following priority order:
- Explicit values passed to
IAMAuth/JWTAuthconstructors. - Environment variables — field defaults like
NEBIUS_API_KEYandNEBIUS_PROJECT_IDare substituted automatically if the corresponding variable is set. auth.ini— if thecontreeCLI is installed, credentials written bycontree authare read from~/.config/contree/auth.ini(or$CONTREE_HOME/$XDG_CONFIG_HOME/contree).
The active profile is taken from the [DEFAULT] section of auth.ini and can be overridden with the CONTREE_PROFILE environment variable.
Objects reusing
You can preconfigure run and then reuse it, for example:
import asyncio
from contree_sdk import Contree
async def amain():
contree = Contree(token="fake-token")
image = await contree.images.use("busybox:latest")
# preconfigure a run that generates random string and writes to file
preconfigured_run = image.run(shell="echo $RANDOM > /tmp/random.txt")
# reuse it multiple times
result1 = await preconfigured_run
result2 = await preconfigured_run
result3 = await preconfigured_run
# each execution will generate different uuid, because each result is gonna be unique
asyncio.run(amain())
File uploading
import asyncio
from contree_sdk import Contree
async def amain():
contree = Contree(token="fake-token")
# upload file
file = await contree.files.upload("/some/local/file.txt")
print(file.uuid)
asyncio.run(amain())
License
Copyright 2026 Nebius B.V.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Apache and the Apache logo are either registered trademarks or trademarks of The Apache Software Foundation in the United States and/or other countries.
Metadata
Release files for contree-sdk 0.3.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| contree_sdk-0.3.6.tar.gz | 43.9 kB | With provenance | Details for contree_sdk-0.3.6.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| contree_sdk-0.3.6-py3-none-any.whl | Python 3 | none | any | Details for contree_sdk-0.3.6-py3-none-any.whl |
Total release size: 112.0 kB
Release files / contree_sdk-0.3.6.tar.gz
| Download URL | contree_sdk-0.3.6.tar.gz |
|---|---|
| Size | 43.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a0c779f954b869c4de5b732e9d777d186b5cf2bc81105be4a89e509574cc46e9
|
|
BLAKE2b-256 checksum How to use checksums |
185c1b1e3c26bc4adee7688228b8ec9928ef6a8683fdf66c0567fa999569b76a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logRelease files / contree_sdk-0.3.6-py3-none-any.whl
| Download URL | contree_sdk-0.3.6-py3-none-any.whl |
|---|---|
| Size | 68.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f58b760e14b61691a2f05e69b8e0e98d2b1fc90a932f4cf44d0cdf1c473276a8
|
|
BLAKE2b-256 checksum How to use checksums |
edef19356bf763fd388dd669f47b199fd8ff67497a3a52c720c30993a06fb8b3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logSecurity for contree-sdk 0.3.6
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for contree_sdk-0.3.6.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.