```
Re-running the same command without `--no-cache` produces layer cache
hits, and the `ADD URL` step short-circuits at the `HEAD` probe (look
for `URL cache hit (HEAD validators match)` in the log) – no body
download, no upload.
## See also
* [run - Execute a command in the sandbox](./run) – the single-shot version of what `RUN` does.
* [session - Manage sessions, branches, history](./session) – inspect or branch the layer history.
* [images - List and import images](./images) – list, import, and tag images directly.
# cat - Show file content from the image
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/cat
Display the contents of a file from the session image.
## Examples
```bash theme={null}
# View a text file
contree cat /etc/os-release
# Pipe to another command
contree cat /var/log/app.log | grep ERROR
# Redirect to a local file
contree cat /etc/nginx/nginx.conf > nginx.conf
```
## Help output
usage: contree cat \[-h] path
Show file content from the session image.
Downloads and displays a file from the current session image via the
/inspect/ API without spawning an instance. Binary files are refused
when stdout is a terminal — use shell redirection or \`contree cp\` to
save them locally.
Results are cached per (image, path) so repeated reads are instant.
positional arguments:
path Path inside image
options:
-h, --help show this help message and exit
for coding agents:
read-only command (inspect API, no instance spawn)
binary output is blocked on interactive TTY; pipe or use cp for binaries
--format is ignored; output is raw file content
agent note:
Before using this command in an automated workflow, read:
contree agent
## Behavior
The file is read directly from the image – no sandbox is started.
Binary files are detected and refused when output is a terminal (to protect
your shell). Redirect to a file or pipe to another command to handle binary
content:
```bash theme={null}
contree cat /usr/bin/curl > curl
```
For downloading files to a specific local path, use [cp - Download a file from the image](./cp) instead.
## See also
* [ls - List files in the image](./ls) – list files before viewing
* [cp - Download a file from the image](./cp) – download to a local path with progress
# cd - Change session working directory
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/cd
Change the working directory for subsequent commands in the current session.
## Examples
```bash theme={null}
contree cd /app
contree run -- ls # runs in /app
contree cd /etc
contree cat os-release # reads /etc/os-release
contree cd # print current working directory
```
## Help output
usage: contree cd \[-h] \[path]
Change the working directory in the current session.
Sets the session's cwd used by subsequent commands (run, ls, cat).
Relative paths are resolved against the current cwd. Without an
argument, prints the current working directory.
The path is validated against the image filesystem via the inspect API.
positional arguments:
path Target directory
options:
-h, --help show this help message and exit
for coding agents:
mutates local session cwd pointer
validates path exists in image via inspect API
agent note:
Before using this command in an automated workflow, read:
contree agent
## Behavior
`cd` stores the path in the session state. Subsequent `run`, `ls`, `cat`,
and `cp` commands resolve relative paths against it.
`cd` without arguments prints the current working directory.
`cd` validates the target against the image filesystem via the
inspect API and reports an error when the directory does not exist.
# cp - Download a file from the image
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/cp
Download a file from the session image to a local path.
## Examples
```bash theme={null}
# Copy a config file locally
contree cp /etc/nginx/nginx.conf ./nginx.conf
# Download a build artifact
contree cp /app/dist/output.tar.gz ./output.tar.gz
```
## Help output
usage: contree cp \[-h] path dest
Copy a file from the session image to a local path.
Downloads the file at PATH inside the current session image and writes
it to DEST on the local filesystem. Progress is logged for large files.
Unlike \`cat\`, this command handles binary content and does not require
a terminal.
positional arguments:
path Path inside image
dest Local destination path
options:
-h, --help show this help message and exit
for coding agents:
read-only command against remote image, writes local file DEST
suitable for binary files
--format is ignored; command writes bytes directly
agent note:
Before using this command in an automated workflow, read:
contree agent
## Behavior
The file is streamed from the image directly – no sandbox is started.
For large files, progress is logged every 5 seconds with download speed
and ETA. The final log line shows total size and average speed.
## See also
* [ls - List files in the image](./ls) – list files to find the path
* [cat - Show file content from the image](./cat) – view file contents without downloading
* [Your First Sandbox](../tutorial/first-steps) – downloading files
# env - Manage session environment variables
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/env
Manage session-level environment variables. Variables set with `env` are
applied to every `contree run` automatically. Per-run `-e` flags override
session env vars with the same key.
## Examples
```bash theme={null}
# Set PATH after installing tools
contree env PATH=/root/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/sbin
# Set multiple variables
contree env DEBUG=1 DB_HOST=localhost
# List current session env
contree env
# Unset variables
contree env -U PATH
contree env -U DEBUG DB_HOST
# Per-run -e overrides session env
contree run -e DEBUG=0 -- ./app
```
## Help output
usage: contree env \[-h] \[-U] \[KEY=VALUE ...]
Manage session environment variables.
Session env vars are applied to every \`contree run\` automatically.
Per-run \`-e\` flags override session env vars with the same key.
positional arguments:
KEY=VALUE Environment variables to set (or keys to delete with -d)
options:
-h, --help show this help message and exit
-U, --delete, --rm Unset the specified environment variables
examples:
contree env list session env vars
contree env PATH=/root/.cargo/bin:\$PATH set PATH
contree env DEBUG=1 DB\_HOST=localhost set multiple
contree env -U PATH unset PATH
contree env -U PATH DEBUG unset multiple
agent note:
Before using this command in an automated workflow, read:
contree agent
## Behavior
Session env vars are stored in SQLite per session. They persist across
terminal restarts (when using `-S` or `CONTREE_SESSION`).
When `contree run` builds the payload, it merges:
1. Session env vars (base)
2. Per-run `-e` flags (override)
Deleting a session (`session delete`) removes its env vars.
Values with `=` in them work correctly — only the first `=` is the
separator: `contree env CMD=a=b=c` sets `CMD` to `a=b=c`.
# file - Stage file edits for the next run
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/file
Stage file changes for the next `contree run`. Pending files are
automatically included without needing `--file` flags.
## Examples
```bash theme={null}
# Edit a file from the image in $EDITOR
contree file edit /etc/nginx/nginx.conf
# Stage a local file at a specific path
contree file cp ./config.yaml /etc/app/config.yaml
# Both edits apply on the next run
contree run nginx -t
```
## Help output
usage: contree file \[-h] \{edit,e,cp,ls,list} ...
Manage files in the session image.
Subcommands:
edit (e) Download a file from the session image, open it in \$EDITOR
(or vi), and upload the modified version as a pending file
attachment. The change takes effect on the next \`run\`.
cp Copy a local file into the session image as a pending file
attachment. The file is uploaded immediately but injected
into the sandbox on the next \`run\`.
Pending files are branch-aware — switching branches changes which
files are visible.
positional arguments:
\{edit,e,cp,ls,list}
edit (e) Edit a file in the session image
cp Copy a local file into the session image
ls (list) List uploaded files (joined with local cache)
options:
-h, --help show this help message and exit
for coding agents:
mutating command (stages pending file changes for next run)
file edit PATH uses local editor and uploads on change
file cp SRC DEST uploads local file and stages DEST path
agent note:
Before using this command in an automated workflow, read:
contree agent
## Subcommands
### `file edit`
`contree file edit PATH` (alias `e`) downloads the file at `PATH` from
the session image, opens it in `$EDITOR` (defaults to `vi`), and stages
the modified buffer as a pending upload that will be injected into the
next `contree run`. Missing files are created as empty buffers so the
command doubles as `touch + open`.
\$ contree file edit --help
usage: contree file edit \[-h] \[-E EDITOR] path
Edit a remote file via a local editor. The updated content is uploaded and staged as a pending
file for the next run.
positional arguments:
path Path inside image
options:
-h, --help show this help message and exit
-E, --editor EDITOR Editor command (default: /usr/bin/vim)
for coding agents: mutates session state (adds pending file entry) does not apply immediately;
effect appears on next \`contree run\`
### `file cp`
`contree file cp SRC DEST` (alias `f`) reads a local file at `SRC`, uploads
it to the project’s file store, and stages it for delivery at `DEST` inside
the session image on the next `contree run`. Use this when you have a file
ready on disk locally and just want it materialised inside the sandbox
without spawning an instance first.
\$ contree file cp --help
usage: contree file cp \[-h] src dest
Upload a local file and stage it as a pending attachment to be injected on the next run.
positional arguments:
src Local file path
dest Destination path inside image
options:
-h, --help show this help message and exit
for coding agents: mutates session state (adds pending file entry) destination path is inside
sandbox filesystem
### `file ls`
`contree file ls` lists files uploaded to the project (`GET /v1/files`)
and joins each row with the local upload cache. The `SOURCE` column shows
whatever this machine produced the file from:
* absolute host path for files uploaded via `run --file` or `COPY`;
* `https://...` URL for files fetched via `ADD URL`.
`SOURCE` resolves **only for files uploaded from this very machine**.
The mapping lives in the local SQLite cache (per-profile, under
`$CONTREE_HOME/cli/sessions/.db`) keyed by
`path + inode + mtime + size` (host paths) or by the URL itself (URL
fetches). It is **not** synced anywhere, so a row will show an empty
`SOURCE` whenever:
* the file was uploaded by a different machine, container, or teammate;
* the file was uploaded by an earlier CLI version that did not yet
track its origin (those entries backfill the next time the file is
matched by the local cache);
* the host file has been moved, renamed, or its `inode/mtime/size` has
changed since upload (the cache key no longer matches and the
mapping is treated as missing until the next upload).
There is no way to recover the source of a file uploaded from another
machine – the server stores only `uuid`, `sha256`, `size`,
`created_at`, and `updated_at`.
```bash theme={null}
contree file ls
contree file ls --since 1d --limit 200
contree file ls -q # uuid + sha256 + source only
contree -o json file ls | jq 'select(.source != "")'
```
\$ contree file ls --help
usage: contree file ls \[-h] \[--since SINCE] \[--until UNTIL] \[--limit LIMIT] \[-q]
List remote files uploaded to the project and, when present in the local upload cache, show what
produced them under the 'source' column: either an absolute host path (for run --file / COPY
uploads) or a URL (for ADD URL). source is THIS-MACHINE ONLY: the mapping lives in the local CLI
cache (\$CONTREE\_HOME/cli/sessions/\.db) and is never synced. Files uploaded from a
different host, by a teammate, or before tracking landed will show an empty source -- that is
expected, not a bug. Use the remote uuid or sha256 for cross-machine identity.
options:
-h, --help show this help message and exit
--since SINCE Parse +/- intervals (bare seconds or smhdMy) or ISO/date to UTC datetime.
--until UNTIL Show files before. Parse +/- intervals (bare seconds or smhdMy) or ISO/date to
UTC datetime.
--limit LIMIT Stop after this many files and warn if more are available
-q, --quiet Emit only uuid, sha256, and source columns. source is populated only for files
uploaded from this very machine.
examples: contree file ls contree file ls --since 1d contree file ls --limit 5000 contree file ls
-q # uuid + sha256 + source contree -o json file ls
## Pending files
Pending files accumulate until the next `contree run` consumes them.
Explicit `--file` flags on `contree run` take priority over pending files
at the same path.
Files are uploaded with SHA256 dedup – identical content is not re-uploaded.
## See also
* [Working with Files](../tutorial/files) – full tutorial on file injection and editing
* [run - Execute a command in the sandbox](./run) – the `--file` syntax for inline file injection
# images - List and import images
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/images
List images in the project. Images are the filesystem snapshots that sandboxes
run from – every non-disposable `contree run` produces a new one.
## Examples
```bash theme={null}
# List all images
contree images
# Filter by tag prefix
contree images --prefix=ubuntu
# Include untagged intermediate images too
contree images -a
# Images created in the last hour
contree images --since=1h
# Find a specific image by UUID
contree images --uuid=3f2a7b1c-9d2e-4f60-8a1b-5c3d7e9f0a2b
# JSON output for scripting
contree -o json images | jq -r '.tag'
```
## Help output
usage: contree images \[-h] \[--prefix PREFIX] \[-i UUID] \[-a] \[--since SINCE] \[--until UNTIL]
\[--limit LIMIT]
\{list,ls,import} ...
List and import sandbox images.
Without a subcommand, lists images (same as \`\`images list\`\`).
Subcommands:
list (ls) List images with filtering and pagination
import Import image from a container registry
positional arguments:
\{list,ls,import}
list (ls) List images
import Import image from container registry
options:
-h, --help show this help message and exit
--prefix PREFIX Filter by tag prefix
-i, --uuid UUID Filter by image UUID
-a, --all Include untagged images (default: tagged only)
--since SINCE Parse +/- intervals (bare seconds or smhdMy) or ISO/date to UTC datetime.
--until UNTIL Show images before. Parse +/- intervals (bare seconds or smhdMy) or ISO/date
to UTC datetime.
--limit LIMIT Stop after this many images and warn if more are available (default: 3000)
examples:
contree images --prefix=ubuntu
contree images list --all
contree images import ubuntu:latest
contree images import ubuntu:\{latest,noble,jammy}
contree images import ghcr.io/owner/image:tag
for coding agents:
\`images\` / \`images list\` is read-only
\`images import\` spawns async import operations and polls until completion
supports brace expansion for batch imports
Ctrl+C cancels all active import operations
agent note:
Before using this command in an automated workflow, read:
contree agent
## Filtering
`--prefix` matches the beginning of the tag string. This is useful for
browsing available base images:
```bash theme={null}
contree images --prefix=python
contree images --prefix=common/
```
`--since` and `--until` accept either ISO timestamps or duration intervals
like `1h`, `30m`, `7d`.
## Subcommands
### `images list`
`contree images list` (alias `ls`) is the explicit form of the bare
`contree images` invocation. Both share the same flag set – pick the
explicit form when you want a command that reads symmetrically with
`images import`, or in scripts that already use the subcommand style
everywhere.
\$ contree images list --help
usage: contree images list \[-h] \[--prefix PREFIX] \[-i UUID] \[-a] \[--since SINCE] \[--until UNTIL]
\[--limit LIMIT]
options:
-h, --help show this help message and exit
--prefix PREFIX Filter by tag prefix
-i, --uuid UUID Filter by image UUID
-a, --all Include untagged images (default: tagged only)
--since SINCE Parse +/- intervals (bare seconds or smhdMy) or ISO/date to UTC datetime.
--until UNTIL Show images before. Parse +/- intervals (bare seconds or smhdMy) or ISO/date to
UTC datetime.
--limit LIMIT Stop after this many images and warn if more are available (default: 3000)
### `images import`
`contree images import REF [REF ...]` pulls one or more images from an
external OCI registry into the project and waits for the import
operation to finish. Each reference may be a `docker://` URL or any
form the platform accepts; multiple refs are imported sequentially with
shared credentials, and Ctrl-C cancels the in-flight operation cleanly.
\$ contree images import --help
usage: contree images import \[-h] \[--username USERNAME] \[--password PASSWORD] \[-t TIMEOUT]
refs \[refs ...]
positional arguments:
refs Image references (supports brace expansion)
options:
-h, --help show this help message and exit
--username USERNAME Registry username (enables credentials)
--password PASSWORD Registry password (prompted securely if --username given)
-t, --timeout TIMEOUT
Import timeout in seconds
examples:
contree images import ubuntu:latest
contree images import --timeout 600 ubuntu:latest
contree images import docker.io/ubuntu:latest
contree images import docker://docker.io/ubuntu:latest
contree images import ghcr.io/ubuntu/ubuntu:latest
contree images import ubuntu:\{latest,noble,jammy}
for coding agents:
mutating command — creates import operations
all formats are normalised to docker://registry/path:tag
polls every 5 seconds until all operations complete
Ctrl+C cancels all active import operations
## See also
* [tag - Tag or untag an image](./tag) – assign a tag to an image
* [Your First Sandbox](../tutorial/first-steps) – browsing and choosing images
# Command Reference
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/index
usage: contree \[-h] \[-v] \[-p PROFILE] \[--token TOKEN] \[-u URL] \[-P PROJECT] \[-c CONFIG\_PATH]
\[-L \{debug,info,warning,error,critical}]
\[-o \{csv,default,json,json-pretty,plain,table,toml,tsv}] \[-S SESSION\_KEY]
\{use,ci,run,r,build,bd,images,i,img,tag,t,ps,kill,show,operation,op,ls,cat,cp,export,file,f,session,s,auth,skill,cd,env,agent,man,shell,sh} ...
ConTree CLI - command-line client for the ConTree sandbox platform.
Run sandboxes, manage images, inspect filesystems, and track operations
through the ConTree REST API.
Authentication:
Bearer token + project ID. Default API URL:
[https://api.tokenfactory.nebius.com/sandboxes/](https://api.tokenfactory.nebius.com/sandboxes/)
Use \`contree auth --help\` to configure persistent credentials.
Coding-agent bootstrap (important):
Agents should read \`contree agent\` before executing task commands.
positional arguments:
\{use,ci,run,r,build,bd,images,i,img,tag,t,ps,kill,show,operation,op,ls,cat,cp,export,file,f,session,s,auth,skill,cd,env,agent,man,shell,sh}
use (ci) Set or show current session image
run (r) Spawn a sandbox instance
build (bd) Build image from Dockerfile
images (i, img) List and import images
tag (t) Tag an image
ps List operations (alias for \`operation ls\`)
kill Cancel operations (alias for \`operation cancel\`)
show Show operation result (alias for \`operation show\`)
operation (op) Manage operations (list/show/cancel)
ls List files in image
cat Show file content from image
cp Copy file from image to local path
export Export image rootfs or a subtree as tar.gz
file (f) Manage files in session image
session (s) Manage session branches and history
auth Configure authentication
skill Manage agent skills
cd Change working directory
env Manage session environment variables
agent (man) Show manual
shell (sh) Interactive shell mode
options:
-h, --help show this help message and exit
-v, --version show program's version number and exit
-p, --profile PROFILE
Use this profile for the current command
--token TOKEN API token (overrides profile for this invocation)
-u, --url URL API base URL (overrides profile for this invocation)
-P, --project PROJECT
Project ID (overrides profile for this invocation)
-c, --config CONFIG\_PATH
Config file path (default: /home/runner/.config/contree/auth.ini)
-L, --log-level \{debug,info,warning,error,critical}
Logging level (default: info)
-o, --format, --output \{csv,default,json,json-pretty,plain,table,toml,tsv}
Output format (default: default)
-S, --session SESSION\_KEY
Session key override (alternative to CONTREE\_SESSION)
examples:
contree use tag:ubuntu:latest set session image
eval \$(contree use tag:ubuntu:latest) set + export env var
contree run -- uname -a run command in session image
contree run --shell -- 'echo hi' shell mode
contree run --file ./app.py:/app.py --disposable -- python /app.py
contree run --file ./src:/app/src -- make -C /app/src
contree images --prefix=ubuntu
contree ps -q
contree op ls same as \`contree ps\`
contree op show UUID1 UUID2 multi-UUID show
contree op cancel UUID1 UUID2 multi-UUID cancel (or --all)
contree show OPERATION\_UUID
contree tag IMAGE\_UUID latest
contree ls /etc list files in session image
contree cat /etc/os-release show file from session image
contree auth save token (secure prompt)
contree auth switch staging
contree man user manual
contree agent coding-agent manual
for users:
contree man
for coding agents (required bootstrap):
1) read: contree agent
2) inspect command syntax: contree \ --help
3) only then execute task commands
before running tasks:
ensure auth exists; if missing/invalid, ask user to run \`contree auth\`
high-signal read-only commands:
contree images | ps | show UUID | ls \[PATH] | cat PATH | session | session show
mutating commands (change remote or local session state):
contree use IMAGE | run -- CMD | file edit PATH | file cp SRC DEST
contree tag UUID TAG | kill UUID | cd PATH | session checkout BRANCH
environment variables:
CONTREE\_PROFILE Active config profile (selects which profile to use)
CONTREE\_SESSION Explicit session name (for multi-terminal workflows).
If unset, contree auto-generates \+\<8hex> (derived
from profile+ppid+tty); export your own for stable
reuse. You can also pass -S/--session instead.
CONTREE\_SESSION\_DB Path to session SQLite database
CONTREE\_NO\_UPDATE\_CHECK Set to any value to disable PyPI update checks
registration-time fallbacks (only read by \`contree auth\`, not at runtime):
CONTREE\_TOKEN / NEBIUS\_API\_KEY Token used when --token is omitted
CONTREE\_URL URL used when --url is omitted
CONTREE\_PROJECT / NEBIUS\_AI\_PROJECT Project ID used when --project is omitted
# kill - Cancel operations
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/kill
**`contree kill` is a top-level shortcut for [operation cancel](./operation) (`contree op cancel`).**
Both share one argparse setup and one handler. The top-level `kill`
accepts the same positional UUIDs and `--all` flag as `op cancel`,
including multiple UUIDs in a single invocation. See the
[operation - Manage operations](./operation) page for the full description.
Cancel running operations. Only active operations (PENDING, ASSIGNED,
EXECUTING) can be cancelled.
## Examples
```bash theme={null}
# Cancel a specific operation
contree kill 3f2a7b...
# Cancel multiple operations in one call
contree kill 3f2a7b... a1b2c3... 9d8e7f...
# Cancel all active operations
contree kill --all
```
## Help output
usage: contree kill \[-h] \[-a] \[UUID\_OR\_REF ...]
Manage operations (list, inspect, cancel).
Aggregates ps/show/kill under a single namespace, and adds multi-UUID
support to \`\`show\`\` and \`\`cancel\`\` so several operations can be acted
on in one invocation.
Subcommands:
list (ls) List operations. \`\`contree ps\`\` is an alias.
show UUID \[UUID...] Show one or more operation results.
cancel UUID \[UUID...] Cancel one or more operations (or --all).
positional arguments:
UUID\_OR\_REF Operations to cancel. Accepts UUIDs and session-history references (HEAD, HEAD\~N,
@, @N, @-N, @+N, :N, bare N).
options:
-h, --help show this help message and exit
-a, --all Cancel every active operation
for coding agents:
list/show are read-only; cancel mutates remote state
show and cancel accept multiple UUIDs in one invocation
show supports @N session-history references inherited from \`contree show\`
agent note:
Before using this command in an automated workflow, read:
contree agent
## Behavior
The CLI sends a `DELETE` request to the API for each UUID. The
operation transitions to `CANCELLED` status. If the sandbox is already
running, execution is interrupted.
`--all` finds and cancels every active operation in the project. When
`--all` is combined with explicit UUIDs, `--all` wins and the explicit
UUIDs are ignored with a `WARNING`.
On per-UUID API errors (e.g. 404 for an unknown UUID), the command
logs the failure and continues with the remaining UUIDs, exiting with
status `1` at the end.
## See also
* [operation - Manage operations](./operation) — the canonical command (`contree kill` is its shortcut)
* [ps - List activity](./ps) — list operations to find UUIDs
* [run - Execute a command in the sandbox](./run) — Ctrl-C during `contree run` also cancels the operation
# ls - List files in the image
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/ls
List files and directories in the session image without spawning a sandbox.
## Examples
```bash theme={null}
# List root directory
contree ls /
# List a specific directory
contree ls /etc/nginx
# JSON output with file metadata
contree -o json ls /usr/bin
```
## Help output
usage: contree ls \[-h] \[path]
List files in the session image.
Uses the /inspect/ API to list directory contents without spawning an
instance. Defaults to the session working directory (set via \`cd\`).
In default format, the API returns a pre-formatted text listing. In
structured formats (json, csv, etc.) the response is cached per
(image, path) for instant repeat queries.
positional arguments:
path Path inside image (defaults to session cwd)
options:
-h, --help show this help message and exit
for coding agents:
read-only command (inspect API, no instance spawn)
defaults to session cwd when PATH is omitted
use -o json for cacheable structured listings
agent note:
Before using this command in an automated workflow, read:
contree agent
## Output
Each entry shows path, size, permissions (octal), owner, group, modification
time, and type (`d` for directory, `l` for symlink, `-` for file).
This command reads the image filesystem directly – no sandbox is started and
no resources are consumed.
## See also
* [cat - Show file content from the image](./cat) – view file contents
* [cp - Download a file from the image](./cp) – download a file locally
* [Your First Sandbox](../tutorial/first-steps) – inspecting the filesystem
# operation - Manage operations
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/operation
Manage operations under a single namespace. Aggregates `ps` (list),
`show` (inspect), and `kill` (cancel), and adds **multi-UUID support** to
`show` and `cancel` so several operations can be acted on in one call.
`op` is the short alias.
## Subcommands
| Subcommand | Aliases | Description |
| ----------------------- | ----------- | ------------------------------------------------------------ |
| `list` | `ls` | List operations. Same flags as `contree ps`. |
| `show UUID [UUID...]` | `sh` | Show one or more operation results. |
| `wait UUID [UUID...]` | `w` | Wait for operations to reach a terminal status (or `--all`). |
| `cancel UUID [UUID...]` | `kill`, `k` | Cancel one or more operations (or `--all`). |
## Examples
```bash theme={null}
# List active operations (same as `contree ps`)
contree op list
contree op ls
contree op ls -a --status FAILED # all flags from ps are accepted
# Inspect a single operation
contree op show 3f2a7b...
# Inspect several operations at once
contree op show 3f2a7b... a1b2c3... 9d8e7f...
# History references (inherited from `contree show`)
contree op show @5 @4 @3
# Cancel one or more operations
contree op cancel 3f2a7b...
contree op cancel a1b2c3... 9d8e7f...
# Cancel every active operation
contree op cancel --all
```
## Help output
The top-level `op` command is a dispatcher: by itself it only prints
usage and routes to the three subcommands described below.
usage: contree operation \[-h] \{list,ls,show,sh,cancel,kill,k,wait,w} ...
Manage operations (list, inspect, cancel).
Aggregates ps/show/kill under a single namespace, and adds multi-UUID
support to \`\`show\`\` and \`\`cancel\`\` so several operations can be acted
on in one invocation.
Subcommands:
list (ls) List operations. \`\`contree ps\`\` is an alias.
show UUID \[UUID...] Show one or more operation results.
cancel UUID \[UUID...] Cancel one or more operations (or --all).
positional arguments:
\{list,ls,show,sh,cancel,kill,k,wait,w}
list (ls) List operations
show (sh) Show one or more operation results
cancel (kill, k) Cancel one or more operations
wait (w) Wait for operations to reach a terminal status
options:
-h, --help show this help message and exit
for coding agents:
list/show are read-only; cancel mutates remote state
show and cancel accept multiple UUIDs in one invocation
show supports @N session-history references inherited from \`contree show\`
agent note:
Before using this command in an automated workflow, read:
contree agent
## `op list` – dynamic columns
`contree op list` (alias `op ls`) accepts the same filter flags as
`contree ps` (`-a`, `--status STATUS`, `-K KIND`, `--since`,
`--until`, `-q`/`--quiet`) and shares its rendering pipeline. Reach
for it when you want the operations namespace to feel symmetric with
the multi-UUID `show` and `cancel`; otherwise `contree ps` is just
as good. `-S` is the global session flag and only works BEFORE the
subcommand.
\$ contree op list --help
usage: contree operation list \[-h] \[-q] \[-a]
\[--status \{P,PENDING,A,ASSIGNED,E,EXECUTING,S,SUCCESS,F,FAILED,C,CANCELLED}]
\[-k \{image\_import,instance}] \[--since SINCE] \[--until UNTIL]
\[-M SHOW\_MAX]
List operations. \`\`contree ps\`\` is an alias of this command.
options:
-h, --help show this help message and exit
-q, --quiet Only show UUIDs, useful for scripting
-a, --all Show all operations (default: active only)
--status \{P,PENDING,A,ASSIGNED,E,EXECUTING,S,SUCCESS,F,FAILED,C,CANCELLED}
Filter by status (default: EXECUTING only, unless -a is used)
-k, --kind \{image\_import,instance}
Filter by operation kind
--since SINCE Parse +/- intervals (bare seconds or smhdMy) or ISO/date to UTC datetime.
--until UNTIL Show operations before. Parse +/- intervals (bare seconds or smhdMy) or
ISO/date to UTC datetime.
-M, --show-max SHOW\_MAX
Show at most this many operations, useful for --all with large history
(default: 1000)
for coding agents: read-only command
The listing renders **every scalar top-level field** the API returns,
not a hard-coded subset. When the server adds a new field (for example
`cost`, `project_id`, `started_at`), it appears in the output without a
CLI release. Nested structures (`metadata`, `result`, `tags`) are
filtered out – use `op show UUID` for the detail view.
Known fields are lightly typed:
| Field | Transform |
| ------------------------------------------------------- | -------------------------------------------------- |
| `created_at`, `started_at`, `finished_at`, `updated_at` | parsed to UTC datetime |
| `duration` | wrapped as `timedelta` (`total_seconds()` in JSON) |
| `error` | `None` is rendered as empty string |
Column order follows the API response, with one exception: **`error`
is pinned to the last column**. Long free-form error messages would
otherwise push the rest of the row out of alignment.
## `op show` – multiple UUIDs
Each UUID is fetched and rendered through the same code path as
`contree show`, so cached terminal results and history references work
uniformly. Accepted reference forms (mirroring `session rollback`
syntax with a git-style alias):
* `@`, `:`, or `HEAD` – the operation at the active branch tip.
* `@N` (or `:N`, bare `N`) – absolute history id.
* `@-N`, `:-N`, or `HEAD~N` – walk N steps back from the tip.
* `HEAD~` – shorthand for `HEAD~1`.
* `@+N` (or `:+N`) – walk N steps forward from the tip, picking the
latest child at each branch point.
On API errors (e.g. 404 for an unknown UUID), the command logs the
failure and continues with the remaining UUIDs, exiting with status
`1` at the end.
\$ contree op show --help
usage: contree operation show \[-h] \[--raw] UUID\_OR\_REF \[UUID\_OR\_REF ...]
Fetch and display the result of each given operation. Same per-UUID behaviour as \`contree show\`:
terminal results are cached; @N references resolve against session history.
positional arguments:
UUID\_OR\_REF Operations to inspect. Accepts UUIDs and session-history references: @ or HEAD for
the active branch tip, @N for an absolute history id, @-N or HEAD\~N for N steps
back, @+N for N steps forward.
options:
-h, --help show this help message and exit
--raw Print each operation's JSON payload as JSONL (one object per line) to stdout.
Round-trips through the typed operation model, so fields the model doesn't know
about are dropped. Skips formatter routing and derived columns; streams cleanly
into \`jq -c\`. Useful for debugging or for fields the table view omits.
for coding agents: read-only command accepts multiple UUIDs; each rendered as its own row
With table output (`-o table`) and several UUIDs, each operation
currently renders as its own mini-table. Use `default` or `json` for a
unified stream view across multiple UUIDs.
## `op wait` – block until completion
Poll the given operations until each reaches a terminal status
(`SUCCESS`, `FAILED`, `CANCELLED`) and print one row per completion
with the columns `uuid`, `status`, `exit_code`, `timed_out`,
`duration` (and every other scalar field the API returns; `error` is
pinned to the last column).
`--all` waits for every currently active operation in the project.
`--timeout SECONDS` (default `60`) caps the wait — when the deadline
hits, the command emits one extra row per unfinished operation with
`timed_out=true` and the operation’s last observed status (e.g.
`EXECUTING`), then exits with status `1`.
`status` is the server’s word: it reflects orchestration (did the
API run the job?), not what the sandbox process did with its exit
code. The exit code is a separate column. The CLI’s own exit status
is `1` whenever any operation finished non-`SUCCESS`, or the actual
`exit_code` when a `SUCCESS` op exited non-zero — so
`op wait UUID && next-step` composes correctly with sandbox commands
like `run -- false`.
`op wait` is a **pure observer**: it polls operation status and
prints rows, but it **never updates session state**. In particular,
the `detached-` branch created when you ran
`contree run -d` keeps pointing at the **starting** image — `op wait` does not advance it to the result image. The pattern therefore
fits non-image-producing runs (`--disposable`) most cleanly; for
non-disposable fan-out, the result image of each leg lives only on
the server and you must recover it explicitly (see the non-disposable
example below).
`--all` is **project-scoped**. If multiple agents (or multiple shell
sessions) share the same project, `op wait --all` will block on every
active operation across all of them — not just the ones you launched.
The wait still completes correctly; it just waits for more than you
might expect. For multi-agent setups, prefer the explicit
`op wait UUID1 UUID2 ...` form with the UUIDs you actually own.
\$ contree op wait --help
usage: contree operation wait \[-h] \[-a] \[-t TIMEOUT] \[UUID\_OR\_REF ...]
Poll the given operations until each reaches a terminal status (SUCCESS, FAILED, CANCELLED) and
print one row per completion. With --all, waits for every currently active operation (PENDING,
ASSIGNED, EXECUTING).
positional arguments:
UUID\_OR\_REF Operations to wait for. Accepts UUIDs and session-history references
(HEAD, HEAD\~N, @, @N, @-N, @+N, :N, bare N).
options:
-h, --help show this help message and exit
-a, --all Wait for every active operation
-t, --timeout TIMEOUT
Fail with exit code 1 if not all operations reach a terminal status within
this many seconds (default: 60)
for coding agents: read-only command (polls the API; no state mutation) fails with exit code 1 if
--timeout is hit before all complete exit code 1 also when any operation finished non-SUCCESS
Preferred — `--disposable` fan-out, no image to track. Note the
global `-o json` before `run` so `jq` sees JSON; the default
formatter is plain.
```bash theme={null}
A=$(contree -o json run -d --disposable -- pytest tests/a | jq -r .uuid)
B=$(contree -o json run -d --disposable -- pytest tests/b | jq -r .uuid)
C=$(contree -o json run -d --disposable -- pytest tests/c | jq -r .uuid)
contree op wait "$A" "$B" "$C"
contree op show "$A" "$B" "$C" # stdout/stderr per leg
```
Non-disposable fan-out — must recover the chosen leg’s image yourself:
```bash theme={null}
A=$(contree -o json run -d -- apt-get install -y curl | jq -r .uuid)
B=$(contree -o json run -d -- apt-get install -y wget | jq -r .uuid)
contree op wait "$A" "$B"
# Pull the result image out and bind it back into the session,
# or tag it for later reuse.
IMG_A=$(contree -o json op show "$A" | jq -r .image)
contree use "$IMG_A"
contree tag "$IMG_A" feature/curl-tools
```
Block on the whole project (5 min cap):
```bash theme={null}
contree op wait --all --timeout 300
```
## `op cancel` – multiple UUIDs or `--all`
Either pass UUIDs explicitly or use `--all` to cancel every active
operation (`PENDING`, `ASSIGNED`, `EXECUTING`). Combining both is allowed:
`--all` wins, and the explicit UUIDs are ignored with a `WARNING`. As
with `op show`, errors on individual UUIDs do not abort the run; the
command exits `1` if any cancellation failed.
\$ contree op cancel --help
usage: contree operation cancel \[-h] \[-a] \[UUID\_OR\_REF ...]
Cancel each given operation. With --all, cancels every active operation (PENDING, ASSIGNED,
EXECUTING).
positional arguments:
UUID\_OR\_REF Operations to cancel. Accepts UUIDs and session-history references (HEAD, HEAD\~N,
@, @N, @-N, @+N, :N, bare N).
options:
-h, --help show this help message and exit
-a, --all Cancel every active operation
for coding agents: mutating command pass UUIDs to cancel specific operations or --all for
everything
```bash theme={null}
# Mixed: --all still wins, "ignored-1" is not cancelled
contree op cancel --all ignored-1
```
## Comparison with the top-level commands
`contree ps` and `contree kill` are top-level **shortcuts** that share
the same argparse setup and handler as `op list` / `op cancel`
respectively — there is no separate implementation. `contree show`
keeps its own single-UUID handler (the multi-UUID `op show` wraps it).
| Need | Use |
| -------------------------- | ----------------------------------------------------------------------- |
| List active operations | `contree ps` *or* `contree op ls` |
| Inspect one operation | `contree show UUID` *or* `contree op show UUID` |
| Inspect multiple | `contree op show UUID1 UUID2 ...` |
| Block on multiple | `contree op wait UUID1 UUID2 ...` |
| Block on everything active | `contree op wait --all` |
| Cancel one operation | `contree kill UUID` *or* `contree op cancel UUID` |
| Cancel multiple | `contree kill UUID1 UUID2 ...` *or* `contree op cancel UUID1 UUID2 ...` |
| Cancel everything active | `contree kill --all` *or* `contree op cancel --all` |
## See also
* [ps - List activity](./ps) – top-level shortcut for `op list`
* [show - Inspect an operation](./show) – single-UUID inspect (delegated to by `op show`)
* [kill - Cancel operations](./kill) – top-level shortcut for `op cancel`
* [run - Execute a command in the sandbox](./run) – the command that creates operations
# ps - List activity
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/ps
**`contree ps` is a top-level shortcut for [operation list](./operation) (`contree op ls`).**
Both share one argparse setup and one handler — `ps` exists for the
Docker-like UX. New flags or columns added to `operation list` apply
automatically here. See the [operation - Manage operations](./operation) page for the full
description of dynamic columns, error handling, and multi-UUID
workflows in the operation namespace.
List operations and their statuses. By default shows only active operations
(PENDING, ASSIGNED, EXECUTING).
## Examples
```bash theme={null}
# Show active operations
contree ps
# Show all operations (including completed)
contree ps -a
# UUIDs only (for scripting)
contree ps -q
# Filter by status (note: --status, not -S; -S is the global session flag)
contree ps --status FAILED
# Filter by kind
contree ps -k instance
# Operations from the last hour
contree ps -a --since=1h
# Pipe to other commands
contree ps -q | xargs -I {} contree show {}
```
## Help output
usage: contree ps \[-h] \[-q] \[-a]
\[--status \{P,PENDING,A,ASSIGNED,E,EXECUTING,S,SUCCESS,F,FAILED,C,CANCELLED}]
\[-k \{image\_import,instance}] \[--since SINCE] \[--until UNTIL] \[-M SHOW\_MAX]
Manage operations (list, inspect, cancel).
Aggregates ps/show/kill under a single namespace, and adds multi-UUID
support to \`\`show\`\` and \`\`cancel\`\` so several operations can be acted
on in one invocation.
Subcommands:
list (ls) List operations. \`\`contree ps\`\` is an alias.
show UUID \[UUID...] Show one or more operation results.
cancel UUID \[UUID...] Cancel one or more operations (or --all).
options:
-h, --help show this help message and exit
-q, --quiet Only show UUIDs, useful for scripting
-a, --all Show all operations (default: active only)
--status \{P,PENDING,A,ASSIGNED,E,EXECUTING,S,SUCCESS,F,FAILED,C,CANCELLED}
Filter by status (default: EXECUTING only, unless -a is used)
-k, --kind \{image\_import,instance}
Filter by operation kind
--since SINCE Parse +/- intervals (bare seconds or smhdMy) or ISO/date to UTC datetime.
--until UNTIL Show operations before. Parse +/- intervals (bare seconds or smhdMy) or
ISO/date to UTC datetime.
-M, --show-max SHOW\_MAX
Show at most this many operations, useful for --all with large history
(default: 1000) (default: 1000)
for coding agents:
list/show are read-only; cancel mutates remote state
show and cancel accept multiple UUIDs in one invocation
show supports @N session-history references inherited from \`contree show\`
agent note:
Before using this command in an automated workflow, read:
contree agent
## Operation statuses
| Status | Meaning |
| ----------- | ----------------------------- |
| `PENDING` | Queued, waiting for resources |
| `ASSIGNED` | Assigned to a worker |
| `EXECUTING` | Running |
| `SUCCESS` | Completed successfully |
| `FAILED` | Completed with an error |
| `CANCELLED` | Cancelled by the user |
Without `-a`, only `PENDING`, `ASSIGNED`, and `EXECUTING` are shown.
## Dynamic output columns
`ps` renders every scalar top-level field the API returns (not a fixed
subset), so new server fields appear automatically. `error` is pinned
to the last column. See [operation - Manage operations](./operation) for the full description.
## See also
* [operation - Manage operations](./operation) — the canonical command (`contree ps` is its shortcut)
* [show - Inspect an operation](./show) — inspect a specific operation
* [kill - Cancel operations](./kill) — cancel a running operation
* [Scripting & Automation](../tutorial/workflows) — monitoring and scripting patterns
# run - Execute a command in the sandbox
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/run
Spawn a sandbox instance from the session image and execute a command.
## Help output
usage: contree run \[-h] \[-t TIMEOUT] \[-C CWD] \[-e ENV] \[-H HOSTNAME] \[-D] \[-I] \[-s] \[-F FILE]
\[--file-excludes PATTERN \[PATTERN ...]] \[-T TRUNCATE] \[--preserve-env] \[-d]
\[--use IMAGE]
...
Spawn a sandbox instance from the current session image and execute a command.
Uses the image from the active session (set via \`contree use IMAGE\`),
or an image specified inline via \`\`--use IMAGE\`\`.
Commands are passed after -- separator; without --, the first
positional arg is the command.
By default the CLI polls until the operation reaches a terminal
status (SUCCESS, FAILED, CANCELLED) and prints stdout/stderr.
Use -d/--detach to exit immediately after spawning.
File attachments:
Use --file to inject host files or directories into the sandbox
before execution. Files are uploaded to the API (with SHA256 dedup)
and mounted at the specified instance path. Ownership and
permissions default to host file stat unless overridden.
Note: non-disposable runs persist filesystem changes into a
new image. Files attached once are already part of that image
and do not need re-attachment. Use --disposable to discard
changes after execution.
Format: host\_path\[:instance\_path]\[:uUID]\[:gGID]\[:mMODE]
host\_path all defaults from stat
host\_path:/inst/path point a destination path
host\_path:m0755 override only mode
host\_path:/inst/path:u0:g0:m0755 all explicit
host\_path:uroot:groot uid/gid by name (local)
Tagged options (u/g/m) can appear in any order after host\_path.
instance\_path is detected by its leading /.
For directory attachments, files are walked recursively and default
excludes are applied: .\*, .git, \*.pyc, \_\_pycache\_\_, .venv,
.mypy\_cache, .pytest\_cache, node\_modules, dist, build.
Add extra patterns with --file-excludes.
The CLI also keeps a local upload cache keyed by
path+inode+mtime+size and reuses known file UUIDs to avoid
unnecessary re-upload checks/uploads.
Note: named uid/gid (e.g. uroot) are resolved locally via
pwd/grp — use numeric IDs if unsure about host/sandbox mismatch.
positional arguments:
command\_args Command and arguments (after --)
options:
-h, --help show this help message and exit
-t, --timeout TIMEOUT
Timeout in seconds (default: 120)
-C, --cwd CWD Working directory inside sandbox, absolute path or empty string for use
sandbox WORKDIR (default: )
-e, --env ENV Environment variable KEY=VALUE (repeatable)
-H, --hostname HOSTNAME
Container hostname (default: linuxkit)
-D, --disposable Drop filesystem changes after run
-I, --interpreter Interpreter (shebang) mode. Read the script file given as the first
argument, strip the #! line, and send the body as stdin to /bin/sh -s.
Usage: #!/usr/bin/env -S contree run -I
-s, --shell Join command args into a single shell expression
-F, --file FILE Attach file or directory (repeatable, dirs recurse). Format:
host\[:inst\_path]\[:uUID]\[:gGID]\[:mMODE]. Tagged options (u/g/m) in any
order; uid/gid resolved locally from pwd/grp; defaults from host stat.
--file-excludes PATTERN \[PATTERN ...]
Additional glob exclude patterns for directory attachments (repeatable).
-T, --truncate TRUNCATE
Truncate output to N bytes (default: 65536)
--preserve-env Preserve env vars from previous run (server-side)
-d, --detach, --no-wait
Exit immediately after spawning (do not wait for result)
--use IMAGE Switch session to IMAGE before running (UUID or tag:NAME). Equivalent to
'contree use IMAGE' followed by 'run'. Recorded in session history and can
be rolled back with 'session rollback'. (default: )
examples:
contree use ubuntu && contree run -- uname -a
contree run --use tag:ubuntu:latest -- uname -a
contree run --shell -- 'echo hello && ls /'
contree run -e FOO=bar DEBUG=1 -- ./app
contree run --file ./app.py:/app.py --disposable -- python /app.py
contree run --file ./src:/app/src --file-excludes '\*.log' -- make -C /app/src
contree run -d -- sleep 3600
for coding agents:
\`run\` executes remotely inside the instance image (not on local host)
local files/dirs must be mapped with --file to be available remotely
mutates session image unless --disposable is set
supports directory attachments via --file host\_dir:/instance\_dir
local file cache avoids re-upload when path+inode+mtime+size unchanged
returns command exit code when available
default formatter prints raw stdout/stderr only
use -o json for structured operation metadata
agent note:
Before using this command in an automated workflow, read:
contree agent
## Quick start with `--use`
Switch session to an image and run a command in one step:
```bash theme={null}
contree run --use tag:ubuntu:latest -- uname -a
```
This is equivalent to:
```bash theme={null}
contree use tag:ubuntu:latest
contree run -- uname -a
```
The image switch is recorded in session history and can be rolled back
with `contree session rollback`.
## Execution modes
**Direct command** (default):
```bash theme={null}
contree run uname -a
```
**Shell mode** (`-s` / `--shell`):
```bash theme={null}
contree run -s -- 'echo hello && ls /'
```
Joins all command args into a single shell expression.
**Interpreter mode** (`-I` / `--interpreter`):
```bash theme={null}
contree run -I ./script.sh
```
Reads a local script, strips the `#!` line, and sends the body as stdin
to `/bin/sh -s`. Enables shebang scripts:
```bash theme={null}
#!/usr/bin/env -S contree run -I
echo "runs inside a ConTree sandbox"
```
**Piped stdin**:
```bash theme={null}
echo 'uname -a' | contree run /bin/sh
```
When stdin is not a TTY, it is read, base64-encoded, and sent as the
`stdin` field.
## Lifecycle
1. Resolve the session image (or switch to `--use IMAGE` first)
2. Upload any `--file` attachments (with SHA256 dedup)
3. Merge pending files from `contree file edit`/`cp`
4. POST `/v1/instances`
5. Poll until terminal status (unless `-d`)
6. Print stdout/stderr; propagate the exit code
On Ctrl-C the operation is cancelled via DELETE.
See [Working with Files](../tutorial/files) for `--file` syntax details.
# session - Manage sessions, branches, history
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/session
Manage session branches and history. Sessions track the image state as you
run commands, with support for branching and rollback.
## Examples
```bash theme={null}
# Show current session
contree session
# List all sessions
contree session list
# Show full history
contree session show
# Create and switch to a branch
contree session branch experiment
contree session checkout experiment
# Switch back
contree session checkout main
# Create a branch from another branch
contree session branch hotfix --from main
# List branches (* marks active)
contree session branch
# Undo last operation
contree session rollback
# Undo last 3 operations (`--` stops argparse from eating `-3` as a flag)
contree session rollback -- -3
# Forward one entry
contree session rollback +1
# Absolute jump to a specific history id (use `session show` first)
contree session rollback 42
# Import image from another session
contree session use other-session
# Delete a session
contree session delete my-old-session
contree session rm my-old-session -y
```
## Help output
\$ contree session --help
usage: contree session \[-h]
\{list,ls,use,branch,br,checkout,co,rollback,rb,show,wait,delete,rm,del} ...
Manage session branches and history.
Without a subcommand, shows the current session info (key, branch,
image, last operation).
Subcommands:
list (ls) List all sessions
use KEY Import another session's current image
branch (br) List or create branches (--from to fork)
checkout (co) Switch active branch
rollback (rb) Navigate history: N=absolute, -N=back, +N=forward
show Display the session history DAG
positional arguments:
\{list,ls,use,branch,br,checkout,co,rollback,rb,show,wait,delete,rm,del}
list (ls) List all sessions
use Import another session's image
branch (br) List, create, delete, or prune branches
checkout (co) Switch active branch
rollback (rb) Navigate history: N=absolute, -N=back, +N=forward
show Show session history
wait Drain detached ops in the current session
delete (rm, del) Delete sessions by key
options:
-h, --help show this help message and exit
for coding agents:
session (no subcommand) is read-only
branch/checkout/rollback/session use mutates local session pointers
\`session show\` defaults to last 20 history entries; pass -a/--all for full DAG
use \`session show\` to inspect history DAG before destructive navigation
\`session wait \[OPS...]\` waits for active or specified operations
agent note:
Before using this command in an automated workflow, read:
contree agent
## Concepts
Each non-disposable `contree run` creates a new history entry and advances
the branch pointer. Branches share the underlying history – creating a
branch just adds a new pointer at the current position.
Rollback moves the branch pointer backwards. History entries are preserved
and can be recovered by creating a new branch.
## Subcommands
### `session list`
`contree session list` (alias `ls`) prints every session known to the
current profile, with the active session marked. The optional
`--filter` flag narrows the list by substring match against the session
key, which is handy when you keep many disposable sessions named after
features or tickets.
\$ contree session list --help
usage: contree session list \[-h] \[--filter FILTER\_TEXT]
List locally known sessions and their current branch/image.
options:
-h, --help show this help message and exit
--filter FILTER\_TEXT Filter session keys containing this text
for coding agents: read-only command
### `session use`
`contree session use KEY` imports the **current image** of another
session into the active session as a new history entry. The source
session is not modified; this is a “fork the snapshot, keep working
here” operation, distinct from the top-level `contree use` which starts
or resumes a session against an image reference.
\$ contree session use --help
usage: contree session use \[-h] session\_name
Set current session image to another session's tip image. Accepts exact key or key suffix.
positional arguments:
session\_name Session key or suffix to match
options:
-h, --help show this help message and exit
for coding agents: mutates current session history
### `session branch`
`contree session branch` (alias `br`) lists branches with `*` marking
the active one. Pass a name to create a new branch pointing at the
current history position, or combine with `--from BRANCH` to fork off a
different branch. The `-U`/`--prune` flag removes branches that no
longer reference live history.
\$ contree session branch --help
usage: contree session branch \[-h] \[--from FROM\_BRANCH] \[-U] \[--prune] \[branch\_name]
List branches (no args). Create with NAME (optionally --from). Delete with --delete NAME. Prune
disposable-/detached- branches with --prune.
positional arguments:
branch\_name Branch name (create/delete target)
options:
-h, --help show this help message and exit
--from FROM\_BRANCH Source branch (default: active branch)
-U, --delete, --rm Delete the specified branch (NAME required, must not be active)
--prune Prune disposable-/detached- branches (non-active only)
for coding agents: read-only when NAME/--delete/--prune omitted mutating when
creating/deleting/pruning
### `session checkout`
`contree session checkout BRANCH` (alias `co`) switches the active
branch pointer. Working directory, pending files, and the current
image are all reset to whatever the target branch currently points at,
so it is the safe way to bounce between parallel experiments.
\$ contree session checkout --help
usage: contree session checkout \[-h] checkout\_branch
Move current session to another existing branch tip.
positional arguments:
checkout\_branch Branch to switch to
options:
-h, --help show this help message and exit
for coding agents: mutates active branch pointer
### `session rollback`
`contree session rollback [TARGET]` (alias `rb`) navigates the history
of the current branch. With no argument it steps back one entry; a
positive number jumps to that absolute history index, `-N` steps back
`N` entries, and `+N` steps forward. History entries are preserved –
rollback only moves the branch pointer.
\$ contree session rollback --help
usage: contree session rollback \[-h] \[target]
Move branch pointer in session history. Supports absolute ID, relative backward (-N), and forward
(+N).
positional arguments:
target History target: ID (absolute), -N (back), +N (forward) — use \`-- -N\` for negative
values
options:
-h, --help show this help message and exit
for coding agents: mutates active branch history pointer
### `session show`
`contree session show` prints the session history DAG with one row per
entry, including operation IDs, image UUIDs, branch pointers, and
relative timestamps. Use `-a` to include hidden entries, `-k KIND` to
filter by entry kind (e.g. `run`, `cd`), and `-l LAST` to show only the
last N rows.
\$ contree session show --help
usage: contree session show \[-h] \[-a] \[-k KIND] \[-l LAST] \[--since SINCE] \[--until UNTIL]
\[session\_name]
Print session history DAG entries and branch labels. By default shows last 20 entries; use
-a/--all for full history.
positional arguments:
session\_name Session key or suffix (default: current session)
options:
-h, --help show this help message and exit
-a, --all Show full history (default: last 20 entries)
-k, --kind KIND Filter history entries by kind (e.g., run, use, cd)
-l, --last LAST Show last N entries after filtering
--since SINCE Show entries since. Parse +/- intervals (bare seconds or smhdMy) or ISO/date to
UTC datetime.
--until UNTIL Show entries before. Parse +/- intervals (bare seconds or smhdMy) or ISO/date
to UTC datetime.
for coding agents: read-only command
### `session wait`
`contree session wait [OP_ID ...]` blocks until the specified operations
reach a terminal state (`SUCCESS`, `FAILED`, or `CANCELLED`). When no
IDs are given it waits for every active operation in the session, which
is the canonical way to drain background `contree run -d` jobs before
moving on.
\$ contree session wait --help
usage: contree session wait \[-h] \[UUID\_OR\_REF ...]
Drain detached operations in the current session. With no arguments, reads the session's pending-
ops cache, polls each to a terminal status, and advances the active branch to each non-disposable
result image (recording \`disposable-\\` branches for disposable runs). With explicit UUIDs,
this command degrades to a plain polling loop: it prints completion rows but does NOT touch the
active branch, because the pending metadata is not loaded for explicit UUIDs.
positional arguments:
UUID\_OR\_REF Operations to wait for (default: all active in this session). Accepts UUIDs and
session-history references (HEAD, HEAD\~N, @, @N, @-N, @+N, :N, bare N).
options:
-h, --help show this help message and exit
for coding agents: no-arg form mutates session history (advances active branch) UUID form is a
pure polling observer if you need result images from explicit UUIDs, use \`op show UUID | jq -r
.image\` and \`contree use\`
### `session delete`
`contree session delete KEY [KEY ...]` (aliases `rm`, `del`) removes
sessions and all their data – history, branches, pending files, shell
history. The command prompts before deleting unless `-y` is passed.
Use this to garbage-collect throwaway sessions; the disk savings on
the SQLite database can be substantial when many short-lived sessions
accumulate.
\$ contree session delete --help
usage: contree session delete \[-h] \[-f] KEY \[KEY ...]
positional arguments:
KEY Session keys to delete
options:
-h, --help show this help message and exit
-f, -y, --force Do not ask for confirmation
```bash theme={null}
contree session delete KEY [KEY ...]
contree session rm KEY -y # skip confirmation
contree session del KEY
```
## See also
* [Sessions, Branches & Rollback](../tutorial/sessions) – full tutorial on sessions, branches, and rollback
* [use - Choose an image for the session](./use) – start or resume a session
# shell - Interactive REPL
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/shell
Start an interactive REPL for managing sessions and running sandbox commands.
## Help output
usage: contree shell \[-h]
Start an interactive shell session.
Launches a REPL where bare commands (e.g. \`apt install curl\`) are
executed in the session sandbox via \`run --shell\`, and prefixed
commands (e.g. \`contree ls /etc\`) are dispatched as management
commands.
Built-in commands: cd, pwd, history, help, exit/quit.
Tab completion for commands, flags, image paths, tags, and branches.
options:
-h, --help show this help message and exit
for coding agents:
bare commands are implicit \`contree run --shell\`
management commands must be prefixed with \`contree\`
exit with \`exit\`/\`quit\` or Ctrl-D
agent note:
Before using this command in an automated workflow, read:
contree agent
## Examples
```bash theme={null}
# Start the shell
contree shell
# Start with a specific output format
contree -o json shell
# Start with a named profile
contree --profile=personal shell
```
## Prompt
The prompt shows the current working directory:
```text theme={null}
contree:/> apt-get update -qq
contree:/app> python main.py
```
## Command dispatch
The shell recognises four types of input:
**Bare commands** — executed inside the sandbox as an implicit `contree run`
with `shell=True`:
```text theme={null}
apt-get install -y curl
echo hello && ls /
```
**Prefixed commands** — `contree ...` dispatches management commands through
the same argparse parser as the CLI:
```text theme={null}
contree ls /etc
contree session branch experiment
contree run -e DEBUG=1 -- ./app
```
**Builtins** – handled locally by the shell:
| Builtin | Description |
| --------------------------- | ------------------------------------------------------------- |
| `cd [PATH]` | Change working directory (`cd -` for previous) |
| `pwd` | Print working directory |
| `history [SEARCH]` | Show command history, optionally filtered by substring |
| `help [TOPIC]` | Show help (optionally for a specific command) |
| `clear` | Clear the terminal screen |
| `timeout DURATION CMD...` | Run `CMD...` with the API operation timeout set to `DURATION` |
| `--format NAME` / `-f NAME` | Change output format (or show current if no argument) |
| `exit` / `quit` | Exit the shell (also Ctrl-D) |
**Aliases** — bare names intercepted for convenience:
| Alias | Equivalent |
| ----------- | --------------------------------------------- |
| `ls [PATH]` | `contree ls [PATH]` (API inspect, no sandbox) |
| `cat PATH` | `contree cat PATH` (API inspect, no sandbox) |
| `vim PATH` | `contree file edit PATH` (with `EDITOR=vim`) |
| `vi PATH` | `contree file edit PATH` (with `EDITOR=vi`) |
| `nvim PATH` | `contree file edit PATH` (with `EDITOR=nvim`) |
| `nano PATH` | `contree file edit PATH` (with `EDITOR=nano`) |
`ls` and `cat` aliases fall back to running inside the sandbox when pending
files exist or when args contain flags or glob characters.
## Implicit run: shell-expression passthrough
Bare commands are forwarded to the sandbox as a single shell expression with
`shell=True`. The entire input line is sent verbatim to the remote `sh -c`,
so operators like `|`, `;`, `&&`, `||`, `>`, `<` are interpreted by the
remote shell exactly as typed:
```text theme={null}
contree:/> mount | grep cgroup
contree:/> echo 1 ; echo 2
contree:/> apt-get update && apt-get install -y curl
contree:/> uname -a > /tmp/info.txt
```
There is no local tokenize/rejoin step, so quoting is preserved:
```text theme={null}
contree:/> python3 -c "print('hello world')"
```
## `timeout` builtin
The shell recognises `timeout DURATION CMD...` and sets the server-side
operation timeout to `DURATION` instead of running the GNU `timeout` binary
inside the sandbox. The kill is enforced by the API, not by a wrapper
process, so the operation surfaces a warning when the limit is hit:
```text theme={null}
contree:/> timeout 30 apk add gcc
contree:/> timeout 5m make build
contree:/> timeout 1h python long_train.py
```
`DURATION` is an integer or decimal optionally followed by a unit suffix:
| Suffix | Meaning |
| ------ | ------- |
| (none) | Seconds |
| `s` | Seconds |
| `m` | Minutes |
| `h` | Hours |
| `d` | Days |
If `DURATION` is not a valid spec (for example `timeout --kill-after=5 30 cmd`
or `timeout --help`), the shell falls through and sends the line to the
sandbox unchanged, so the in-image `timeout` binary still handles advanced
flags.
When the limit is hit, the response carries `state.timed_out=true` and the
shell logs:
```text theme={null}
WARNING: Operation timed out after 30s
```
## Tab completion
The shell provides context-aware tab completion for almost everything
except bare (implicit run) commands. Press Tab to complete:
| Context | What completes |
| ----------------------------------- | ----------------------------------- |
| Empty prompt | All commands, aliases, and builtins |
| `contree ` | Subcommand names |
| `contree CMD -` | Flags for that command |
| `contree CMD --` | Long flags for that command |
| `ls /etc/`, `cat /etc/` | Sandbox file paths |
| `cd /us` | Sandbox directory paths (dirs only) |
| `vim /etc/`, `nano /etc/` | Sandbox file paths |
| `contree use ` | Image UUIDs and `tag:NAME` |
| `contree tag ` | Image UUIDs and `tag:NAME` |
| `contree show ` | Operation UUIDs |
| `contree kill ` | Operation UUIDs |
| `contree session checkout ` | Branch names |
| `contree session branch ` | Branch names |
| `contree session use ` | Session keys |
| `contree file edit ` | Sandbox file paths |
| `help ` | All command and alias names |
Path completions query the sandbox filesystem via the inspect API
and are cached persistently – subsequent completions for the same
directory are instant.
## History search
The `history` builtin takes an optional pattern and filters the
persisted history by case-insensitive substring:
```text theme={null}
contree:/> history # show every entry for this session
contree:/> history apt # any line containing "apt"
contree:/> history 'contree ' # exact "contree " (with trailing space)
contree:/> history make # any line containing "make"
```
History is per-session: searches see only the current `session_key`’s
entries. Up to 10,000 lines are kept; older lines are trimmed on save.
## Line continuation
A trailing `\` at the end of input triggers a `> ` continuation prompt,
just like traditional shells:
```text theme={null}
contree:/> ls \
> -alh \
> /sys
```
Backslash-newline pairs are removed to join the lines into a single
command (`ls -alh /sys`). Unclosed quotes also trigger continuation,
preserving the newline inside the quoted string.
## Limitations
* **No global flags on commands**: `--token`, `--url`, `--log-level` are
not available inside the shell.
* **No local pipes or redirects**: `|`, `>`, `<` are passed as-is to the
sandbox (works for remote commands, not for contree output).
* **No job control**: No `&`, `bg`, `fg`, or Ctrl-Z. Use `contree run -d`
for background tasks.
* **Bare commands use defaults**: `--env`, `--file`, `--disposable`, and
`--detach` require the explicit `contree run` prefix. The operation
timeout has a shorthand: `timeout DURATION CMD...` (see above).
* **No `~` or glob expansion**: Passed as-is to the sandbox.
* **Cannot nest shells**: Running `contree shell` inside a shell is not
supported.
## See also
* [Interactive Shell](../tutorial/shell) – full tutorial on using the interactive shell
* [run - Execute a command in the sandbox](./run) – the `run` command used by implicit bare commands
* [file - Stage file edits for the next run](./file) – the `file edit` command behind editor aliases
# show - Inspect an operation
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/show
**`contree show` is a top-level shortcut for [operation show](./operation) (`contree op show`).**
Both share one argparse setup and one handler. The top-level `show`
accepts one or more UUIDs and history references — each entry renders
as its own row. Accepted reference forms:
* `@`, `:`, or `HEAD` — the operation at the active branch tip.
* `@N`, `:N`, bare `N` — absolute history id.
* `@-N`, `:-N`, `HEAD~N` — N steps back from the tip.
* `HEAD~` — shorthand for `HEAD~1`.
* `@+N`, `:+N` — N steps forward from the tip (latest child).
See the [operation - Manage operations](./operation) page for the full description.
Display the full result of one or more operations, including stdout
and stderr from sandbox execution.
## Examples
```bash theme={null}
# Show a single operation
contree show 3f2a7b...
# Show multiple operations in one call
contree show 3f2a7b... a1b2c3... 9d8e7f...
# History references (resolved against the active session)
contree show @5 @4 @3
# Relative to the active branch tip (like `session rollback`)
contree show @-1 # the operation one step back from the tip
contree show @+1 # the next operation forward (latest child)
# Git-style HEAD notation, equivalent to @ and @-N
contree show HEAD # current tip operation
contree show HEAD~ # one step back (shorthand for HEAD~1)
contree show HEAD~3 # three steps back from the tip
# JSON output for scripting
contree -o json show 3f2a7b...
# Show result of a detached run
contree run -d -- make test
contree show UUID
```
## Help output
usage: contree show \[-h] \[--raw] UUID\_OR\_REF \[UUID\_OR\_REF ...]
Manage operations (list, inspect, cancel).
Aggregates ps/show/kill under a single namespace, and adds multi-UUID
support to \`\`show\`\` and \`\`cancel\`\` so several operations can be acted
on in one invocation.
Subcommands:
list (ls) List operations. \`\`contree ps\`\` is an alias.
show UUID \[UUID...] Show one or more operation results.
cancel UUID \[UUID...] Cancel one or more operations (or --all).
positional arguments:
UUID\_OR\_REF Operations to inspect. Accepts UUIDs and session-history references: @ or HEAD for
the active branch tip, @N for an absolute history id, @-N or HEAD\~N for N steps
back, @+N for N steps forward.
options:
-h, --help show this help message and exit
--raw Print each operation's JSON payload as JSONL (one object per line) to stdout.
Round-trips through the typed operation model, so fields the model doesn't know
about are dropped. Skips formatter routing and derived columns; streams cleanly
into \`jq -c\`. Useful for debugging or for fields the table view omits.
for coding agents:
list/show are read-only; cancel mutates remote state
show and cancel accept multiple UUIDs in one invocation
show supports @N session-history references inherited from \`contree show\`
agent note:
Before using this command in an automated workflow, read:
contree agent
## Output
The command renders every scalar top-level field the API returns
(typically: **uuid**, **kind**, **status**, **created\_at**,
**started\_at**, **finished\_at**, **duration**, **session\_key**, …) and
adds these derived fields:
* **exit\_code** – the sandbox process exit code (extracted from
`metadata.result.state.exit_code`)
* **image** – resulting image UUID from `result.image`
* **tag** – image tag from `result.tag`
* **stdout / stderr** – sandbox output, decoded (for `default`,
`json`, and `json-pretty` formats)
`status` is the server’s word: it reflects whether the API ran the
operation to completion, not whether the sandbox process exited with
zero. A `SUCCESS` row with `exit_code=1` means “the API completed the
job; your command returned 1”. `error` is pinned to the last column.
Nested objects (`metadata`, `result`) are dropped from the flat row
– use `--raw` to keep them, or `-o json` to keep the flat structured
row.
Pass `--raw` to skip all of the above and print each operation’s
JSON payload as JSONL (one object per line) to stdout. The payload
round-trips through the typed operation model, so fields the model
doesn’t know about are dropped – it’s not the server’s byte-for-byte
response. Streams cleanly into `jq -c`. Useful for debugging or
pulling fields the table view omits (resources, full metadata, etc.).
Timestamps come back from the API in UTC and are converted to the
**local timezone** for human-readable formatters (`default`, `table`,
`csv`, `tsv`, `plain`). The JSON formatters preserve the source
timezone offset.
For `csv`, `tsv`, and `table` formats, stdout/stderr are omitted – use
`default` or `json` to see sandbox output.
## See also
* [ps - List activity](./ps) – list operations to find UUIDs
* [operation - Manage operations](./operation) – multi-UUID variant: `contree op show UUID1 UUID2 ...`
* [run - Execute a command in the sandbox](./run) – the command that creates operations
# skill - Install agent skills
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/skill
Install, remove, or upgrade ConTree agent skills for Codex and Claude Code.
## Spec format
Commands accept **specs** – a `kind:hint` URI that identifies the skill type
and target path:
| Spec | Resolves to | Skill type |
| ------------------- | ------------------------------------------------------------------------ | ------------------- |
| `claude:` | `.claude/skills/contree` (project) | ClaudeSkill |
| `claude:~` | `~/.claude/skills/contree` (global) | ClaudeSkill |
| `codex:` | `.codex/skills/contree` (project) | CodexSkill |
| `codex:~` | `~/.codex/skills/contree` (global) | CodexSkill |
| `opencode:` | `.opencode/skills/contree` (project) | OpenCodeSkill |
| `opencode:~` | `$OPENCODE_HOME/skills/contree` (or `~/.config/opencode/skills/contree`) | OpenCodeSkill |
| `amp:` | `.amp/skills/contree` (project) | AmpSkill |
| `amp:~` | `~/.config/agents/skills/contree` (global) | AmpSkill |
| `cline:` | `.cline/skills/contree` (project) | ClineSkill |
| `cline:~` | `$CLINE_DIR/skills/contree` (or `~/.cline/skills/contree`) | ClineSkill |
| `claude-subagent:` | `.claude/agents/contree-subagent.md` (project) | ClaudeSubagentSkill |
| `claude-subagent:~` | `~/.claude/agents/contree-subagent.md` (global) | ClaudeSubagentSkill |
| `claude-agent:` | `.claude/agents/contree.md` (project) | ClaudeAgentSkill |
| `claude-agent:~` | `~/.claude/agents/contree.md` (global) | ClaudeAgentSkill |
| `./path` | `./path` (skill type guessed from path) | auto |
When no specs are given, `install` targets the global (`:~`) variant of
**every** known kind. The Claude-based kinds (`claude`, `claude-agent`,
`claude-subagent`) are skipped unless `~/.claude` already exists, so a
machine without Claude Code installed will not have empty directories
created for it.
## Examples
```bash theme={null}
# Install globally to every known kind (Claude kinds gated on ~/.claude)
contree skill install
# Install into project-level .claude/skills/contree
contree skill install claude:
# Install globally into ~/.claude/skills/contree
contree skill install claude:~
# Install both globally
contree skill install codex:~ claude:~
# Install to explicit path (class guessed from path)
contree skill install ./my/custom/path
# Upgrade all remembered installs
contree skill upgrade
# Upgrade specific target
contree skill upgrade claude:~
# Remove by spec
contree skill remove -y claude:~
# Remove by full path
contree skill remove -y /path/to/skills/contree
# List installs with version and outdated status
contree skill list
contree skill ls
```
## Help output
usage: contree skill \[-h] \{list,ls,install,i,remove,r,rm,del,upgrade,u,update} ...
Install, remove, or upgrade ConTree agent skills.
contree skill install # autodetect agent homes
contree skill install claude:\~ # global \~/.claude
contree skill install codex: # project-level .agents/skills
contree skill install . # project root: every kind under it
positional arguments:
\{list,ls,install,i,remove,r,rm,del,upgrade,u,update}
list (ls) List remembered skill installs
install (i) Install ConTree skill files
remove (r, rm, del)
Remove installed skill files
upgrade (u, update)
Upgrade installed skill files
options:
-h, --help show this help message and exit
agent note:
Before using this command in an automated workflow, read:
contree agent
## Subcommands
### `skill install`
`contree skill install [SPEC ...]` (alias `i`) installs skill directories.
Each spec resolves to a skill class and a filesystem path, both of which
are persisted in `skills.db` so future `list` and `upgrade` calls can
find the install without re-specifying it. With no specs the command
targets the global (`:~`) variant of every known kind – Claude-based
kinds are skipped automatically when `~/.claude` does not exist. Pass
`-y` to overwrite an existing install non-interactively.
\$ contree skill install --help
usage: contree skill install \[-h] \[-f] \[SPEC ...]
positional arguments:
SPEC claude:\~ codex:\~ skill path or project root
options:
-h, --help show this help message and exit
-f, -y, --force Overwrite existing
### `skill list`
`contree skill list` (alias `ls`) shows every remembered install with
its kind, installed version (read from `.version`), the version
bundled with this CLI, an `outdated` flag, and whether the install
path still exists on disk. Stale entries whose path was deleted
externally are pruned from the registry automatically when this
command runs.
\$ contree skill list --help
usage: contree skill list \[-h]
options:
-h, --help show this help message and exit
### `skill upgrade`
`contree skill upgrade [SPEC ...]` overwrites existing installs with
the version bundled in the current CLI. With no specs it upgrades
every remembered location, which is the normal post-`pip install -U`
maintenance step. Targets that are already at the bundled version are
rewritten anyway so any local edits to skill files are reverted.
\$ contree skill upgrade --help
usage: contree skill upgrade \[-h] \[SPEC ...]
positional arguments:
SPEC claude:\~ codex:\~ skill path or project root
options:
-h, --help show this help message and exit
### `skill remove`
`contree skill remove SPEC [...]` (aliases `r`, `rm`, `del`) deletes
installed skill files and forgets the path from the registry. Specs
may be the same URI form accepted by `install`, or a literal filesystem
path. Pass `-y` to skip the confirmation prompt.
\$ contree skill remove --help
usage: contree skill remove \[-h] \[-f] \[SPEC ...]
positional arguments:
SPEC claude:\~ codex:\~ skill path or project root
options:
-h, --help show this help message and exit
-f, -y, --force Do not ask for confirmation
### Install contents
Skill directories contain:
* `.version` — installed package version
* `SKILL.md` — skill prompt with `allowed-tools` frontmatter
* `agents/openai.yaml` — OpenAI-compatible skill config
Skills require `contree` in PATH. If missing, ask the user to install it.
### Skill classes
| Class | Kind | Description |
| --------------------- | ----------------- | ------------------------------------------- |
| `ClaudeSkill` | `claude` | Bundled skill directory for Claude Code |
| `CodexSkill` | `codex` | Bundled skill directory for Codex |
| `OpenCodeSkill` | `opencode` | Bundled skill directory for OpenCode |
| `AmpSkill` | `amp` | Bundled skill directory for Amp |
| `ClineSkill` | `cline` | Bundled skill directory for Cline |
| `ClaudeSubagentSkill` | `claude-subagent` | Standalone `.md` subagent file |
| `ClaudeAgentSkill` | `claude-agent` | Custom agent `.md` with `skills: [contree]` |
# tag - Tag or untag an image
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/tag
Assign or remove a tag from an image. Tags are human-readable names that
make images easier to reference.
## Examples
```bash theme={null}
# Tag an image
contree tag 3f2a7b... my-app:v1.0
# Remove a tag
contree tag 3f2a7b... my-app:v1.0 --delete
# Use the tagged image
eval $(contree use tag:my-app:v1.0)
```
## Help output
usage: contree tag \[-h] \[-U] ARG \[ARG ...]
Assign or remove a tag from an image.
Tags provide human-readable names for image UUIDs, making them easier
to reference in commands like \`contree use tag:NAME\`.
With one argument, tags the current session image.
With two arguments, the first is the image reference and the second is the tag.
Use -U/--delete/--rm to remove a tag instead of assigning one.
positional arguments:
ARG TAG (current image) or IMAGE\_REF TAG
options:
-h, --help show this help message and exit
-U, --delete, --rm Remove tag from image
examples:
contree tag python-dev:latest # tag current session image
contree tag UUID python-dev:latest # tag specific image by UUID
contree tag tag:alpine:latest my-alpine # re-tag by reference
contree tag -U UUID my-tag # remove a tag (or --delete/--rm)
for coding agents:
mutating command
default action assigns tag; use --delete to remove mapping
agent note:
Before using this command in an automated workflow, read:
contree agent
## Usage
Tags are free-form strings. A common convention is `scope/purpose:version`:
```bash theme={null}
contree tag UUID ubuntu-with-curl:latest
contree tag UUID my-project/dev-env:v2
```
Once tagged, reference the image anywhere with the `tag:` prefix:
```bash theme={null}
contree use tag:ubuntu-with-curl:latest
```
Tagging an image that already has a different tag replaces the old tag.
## See also
* [images - List and import images](./images) – list images and their tags
* [Your First Sandbox](../tutorial/first-steps) – working with images
# use - Choose an image for the session
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/commands/use
Set the session image or show the current session state.
This is typically the first command you run – it tells contree-cli which
image to use for subsequent commands.
## Examples
```bash theme={null}
# Start a session with an image
eval $(contree use tag:ubuntu:latest)
# Start a session with a specific image UUID
eval $(contree use 3f2a7b...)
# Start or resume a named session
export CONTREE_SESSION=my-session
# Show current session info
contree use
# Start a fresh session (new session key)
eval $(contree use -N tag:python:3.11-slim)
```
The `eval` wrapper exports `CONTREE_SESSION` into your shell so all
subsequent commands share the same session. Without `eval`, contree prints
the export line but your shell doesn’t pick it up.
## Help output
usage: contree use \[-h] \[-N] \[image]
Set or show the current session image.
With an IMAGE argument, resolves it (UUID or tag:NAME) and sets it as
the active session image. Prints a shell export line so that the
session key can be captured with eval:
eval \$(contree use tag:ubuntu:latest)
Without arguments, displays the current session info (image, branch,
last operation).
Use -N/--new to start a fresh session instead of resuming the current
one. The new session key is printed as an export line.
positional arguments:
image Image UUID or tag
options:
-h, --help show this help message and exit
-N, --new Start a new session instead of resuming the current one
for coding agents:
use IMAGE starts/switches a session and prints CONTREE\_SESSION export
use (without IMAGE) is read-only and prints current session state
use --new IMAGE creates a fresh session key
without CONTREE\_SESSION env var, key is auto-generated as \+\<8hex>
(derived from profile+ppid+tty); export your own for stability
agent note:
Before using this command in an automated workflow, read:
contree agent
## Behavior
**With an image argument**: resolves the image (UUID or `tag:NAME`), sets it
as the session’s current image, and prints a shell export statement.
**Without arguments**: displays the current session info – session key,
active branch, current image, and last operation.
**With `--new`**: generates a new random session key instead of resuming the
existing one. Useful when you want a clean slate in the same terminal.
## Shell detection
The output format adapts to your shell:
* **bash / zsh**: `export CONTREE_SESSION=`
* **fish**: `set -gx CONTREE_SESSION `
Detection uses the `$SHELL` environment variable.
## See also
* [Your First Sandbox](../tutorial/first-steps) – starting your first session
* [Sessions, Branches & Rollback](../tutorial/sessions) – branching and rollback
# Overview
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/index
Command-line client for the [ConTree](https://contree.dev) sandboxing platform.
## What is ConTree?
[ConTree](https://contree.dev) is a secure sandbox API for AI agents with
git-like branching. Every command runs inside a VM-isolated sandbox, and
every execution produces a new **image** – a full filesystem snapshot.
Branch from any checkpoint, explore paths in parallel, pick the winner,
and instantly roll back on failure.
Built for **AI agents that think ahead**:
* **Tree-search execution** – branch the sandbox state so an agent can
explore multiple solution paths in parallel and keep the best one.
* **Instant rollback** – backtrack to any previous checkpoint when a
path fails, without rebuilding from scratch.
* **Safe code execution** – run untrusted or LLM-generated code inside
VM-level isolation. Crashes and side effects stay in the sandbox.
* **Session continuity** – rewind and resume long-running agent
workflows with full filesystem context preserved.
`contree-cli` is the command-line client that talks to the ConTree API.
```bash theme={null}
contree use tag:ubuntu:latest # pick an image for current terminal
contree run apt update -qq # each run snapshots the result
contree run apt install -y curl # builds on the previous snapshot
contree ls /usr/bin/curl # inspect without spawning a VM
```
You can choose name for session by setting the environment variable
`CONTREE_SESSION`.
```bash theme={null}
export CONTREE_SESSION=demo_session # Pick a name for session manually
contree use tag:ubuntu:latest # pick an image for demo_session
contree run -- find /root # run commands
```
## Get started
Step-by-step guide from installation to automated workflows.
Six sections, each building on the previous one.
Every command, flag, and subcommand documented with usage examples.
## Key features
Every run creates a checkpoint. Branch off to experiment, roll back
mistakes, resume from any point.
Map local files into sandboxes with `--file`, edit remote configs
in-place, stage changes for the next run.
JSON, CSV, and TSV output. Detached runs, operation monitoring,
shebang scripts — built for automation.
A single runtime dependency: the `contree-client` library.
Named profiles for different projects and environments. Switch with
a single command.
Browse and download files from sandbox images without spawning a
new instance.
# Building from a Dockerfile
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/tutorial/build
`contree build` turns a familiar `Dockerfile` into a ConTree image. Each
directive becomes one image layer, every layer is a real session
checkpoint, and re-running the same `Dockerfile` reuses prior layers
through a content-addressed cache. This tutorial walks through the
`build-demo` example shipped with the repo so you can see the moving
parts end-to-end.
## The example project
The tree at `docs/examples/build-demo` contains a minimal Python app
plus a Dockerfile that exercises the directives most builds actually
use:
```
docs/examples/build-demo/
├── .dockerignore
├── Dockerfile
├── hello.py
└── src/
├── __init__.py
└── banner.py
```
`hello.py` reads a greeting from an environment variable and prints a
boxed banner; `src/banner.py` provides the box renderer. Nothing
exotic – it just gives the Dockerfile something to `COPY` and `RUN`.
The Dockerfile itself:
```dockerfile theme={null}
FROM python:3.12-alpine
ARG GREETING=hello
ENV APP_GREETING=${GREETING}
WORKDIR /app
COPY hello.py /app/hello.py
COPY src /app/src
ADD https://github.com/nebius/contree-cli/archive/refs/heads/master.zip /tmp/contree-cli.zip
RUN python -c "import sys; print('python', sys.version)"
RUN python -m zipfile -e /tmp/contree-cli.zip /opt/
RUN pip install --no-cache-dir /opt/contree-cli-master
RUN contree --help | head -20
RUN python /app/hello.py
```
Six directives, in order:
1. `FROM python:3.12-alpine` – resolves the base image. If
`tag:python:3.12-alpine` is not already in the project, `contree build`
auto-imports it from the registry.
2. `ARG GREETING=hello` and `ENV APP_GREETING=${GREETING}` – declare a
build-time variable and pin its value into a runtime environment
variable that the app will read.
3. `WORKDIR /app` – sets the working directory for everything below.
4. Two `COPY` directives stage local files (`hello.py` and the `src/`
directory) into the build’s pending uploads.
5. `ADD https://...master.zip /tmp/contree-cli.zip` – streams a remote
archive straight from GitHub into the contree file store, without
creating a local temp file.
6. Five `RUN` directives prove the toolchain works, unpack the zip,
install the CLI from source, and run the demo app.
## Build context and `.dockerignore`
The first positional argument to `contree build` is the **build
context** – the directory that anchors every `COPY` and `ADD` source
path. Anything outside that directory is invisible to the build. In
this example the context is `docs/examples/build-demo`, and the
Dockerfile sits at the top of it.
A `.dockerignore` next to the Dockerfile keeps junk out of the upload:
```text theme={null}
# Demo .dockerignore: keep build context lean
**/*.log
**/__pycache__
.env*
```
The matcher uses the same rule set as `run --file`: `*` is a
single-segment wildcard, `**` crosses directories, `?` matches one
character, and a leading `!` re-includes a previously ignored path.
The last matching rule wins. On top of your `.dockerignore`, the CLI
always filters `.git`, `__pycache__`, `*.pyc`, `.venv`,
`node_modules`, `dist`, and `build`, so you do not need to repeat the
usual suspects.
## Your first build
From the repository root, run:
```bash theme={null}
contree build docs/examples/build-demo --tag contree-cli-build-demo:latest
```
You should see one log line per directive plus a stdout dump after
each `RUN`:
```text theme={null}
[INFO] FROM python:3.12-alpine -> tag:python:3.12-alpine
[INFO] COPY hello.py -> /app/hello.py
[INFO] COPY src -> /app/src
[INFO] ADD https://.../master.zip -> /tmp/contree-cli.zip
[INFO] RUN spawned op=019e... RUN python -c "import sys; print('python', sys.version)"
[INFO] stdout:
python 3.12.13 ...
[INFO] RUN spawned op=019e... RUN python /app/hello.py
[INFO] stdout:
+---------------+
| hello |
| contree build |
+---------------+
[INFO] tagged as contree-cli-build-demo:latest
IMAGE TAG SESSION
contree-cli-build-demo:latest build:
```
The final tagged image is now usable everywhere a tag is accepted:
```bash theme={null}
eval $(contree use tag:contree-cli-build-demo:latest)
contree run python /app/hello.py
```
## Layer cache: the second build is free
Run the same command a second time. Every step prints **cache hit**
and the build finishes in seconds without spawning a single instance.
The cache key for each layer is a chain hash:
```
sha256(parent_layer_hash || state(workdir/env/user/args)
|| directive || pending_files)
```
That means a layer is reused if and only if:
* the previous layer was identical,
* the directive text is byte-for-byte the same,
* the resolved environment (`WORKDIR`, `ENV`, `USER`, `ARG`) matches, and
* for `COPY`/`ADD`, the **content** of the staged files matches (the
SHA-256 of every uploaded file, not their timestamps).
Edit `hello.py`, run the build again, and only the last `RUN` step
plus everything depending on it rebuilds. The earlier `RUN python -c 'import sys; print(sys.version)'` layer is reused because it has no
dependency on `hello.py`.
Each cached layer is materialised as a branch named
`layer:` inside a session keyed by the absolute path of
the context directory: `build:`. So the
cache is **per-context-path** – moving the directory or building from
a sibling worktree starts a fresh cache.
To inspect the layer history:
```bash theme={null}
contree session list --filter build:
contree session use build:
contree session show
```
`contree session show` prints the DAG with one row per layer and the
chain hash visible in the branch column. Switching to a `layer:` branch
puts you on that layer’s image, so you can `contree run` against any
intermediate snapshot to debug a step in isolation.
To force a rebuild ignoring all cached layers:
```bash theme={null}
contree build docs/examples/build-demo --no-cache \
--tag contree-cli-build-demo:latest
```
## Build args and variable substitution
Variables (`$VAR` and `${VAR}`) expand in `FROM`, `RUN`, `COPY`/`ADD`
arguments, `WORKDIR`, `ENV` values, and `USER`. The lookup order is:
1. `--build-arg KEY=VALUE` for any `ARG` already declared.
2. `ENV` directives processed so far.
3. `ARG` defaults from the Dockerfile.
4. Empty string for unknown names.
The demo declares `ARG GREETING=hello` and uses it through
`ENV APP_GREETING=${GREETING}`. Override it at the CLI:
```bash theme={null}
contree build docs/examples/build-demo --build-arg GREETING=ciao \
--tag contree-cli-build-demo:ciao
```
The final `RUN python /app/hello.py` step now prints `ciao` in the
boxed banner because the chain hash of the layer that ran
`ENV APP_GREETING=...` changed, invalidating every layer below it.
## `ADD URL` streams without a temp file
The `ADD` line in the demo points at a GitHub archive:
```dockerfile theme={null}
ADD https://github.com/nebius/contree-cli/archive/refs/heads/master.zip /tmp/contree-cli.zip
```
`contree build` opens the HTTP connection and pipes the response body
**directly** into `POST /v1/files` – the bytes never touch your local
disk. The CLI also remembers the URL’s `ETag`, `Last-Modified`, and
`Content-MD5` validators in the per-context cache. On the next build
it issues a conditional `HEAD` first; if the validators match, the
upload is skipped entirely and the log line reads
`URL cache hit (HEAD validators match)`.
Two things this does **not** do:
* It does not extract tarballs/zips. Use a `RUN python -m zipfile -e`
(or `tar xf`) directive when you need extraction, exactly like the
demo does.
* It does not follow private auth – the request is anonymous. Mirror
the asset to a public URL, or `COPY` it from your build context.
## Supported and skipped directives
The MVP interpreter implements the directives most Dockerfiles
actually rely on:
| Implemented | Notes |
| ---------------------------------------- | ----------------------------------------------------------------------------------- |
| `FROM ref[:tag] [AS name]` | Auto-imports missing tags; `AS name` is parsed but multi-stage is not yet executed. |
| `RUN ...` | Shell-form and JSON exec-form. Spawns one instance per `RUN`. |
| `COPY [--chown=] [--chmod=] SRC... DEST` | Honours `.dockerignore`, dedups by SHA-256. |
| `ADD ...` | Local paths behave like `COPY`; URLs stream through `POST /v1/files`. |
| `WORKDIR`, `ENV`, `ARG`, `USER` | Accumulated and applied to subsequent steps. |
Directives that are parsed and **skipped with a warning** (the build
continues, the image is still produced):
`CMD`, `ENTRYPOINT`, `LABEL`, `EXPOSE`, `VOLUME`, `STOPSIGNAL`,
`MAINTAINER`, `HEALTHCHECK`, `ONBUILD`, `SHELL`,
`COPY --from=stage`.
ConTree images are filesystem snapshots, not OCI runtime configs, so
`CMD`/`ENTRYPOINT` have nowhere to live – you express the entrypoint
explicitly at `contree run` time instead.
## When to reach for `build` vs `run`
The same image you can produce with `contree build` can be produced
by hand with a sequence of `contree run` calls. Pick the right tool:
| Situation | Prefer |
| ------------------------------------------------------------------ | ----------------------------------------------------------------------- |
| You already have a working `Dockerfile` | `contree build` – just reuse it. |
| You want reproducible, cacheable setup driven from version control | `contree build`. |
| You are still experimenting and do not know the final steps | `contree run` interactively; tag a checkpoint when you are happy. |
| You need `CMD`/`ENTRYPOINT`/`HEALTHCHECK` semantics | Neither – those are runtime concerns for OCI runtimes, not for ConTree. |
| You want multi-stage builds today | Not yet – stage `AS` parses but is skipped. Track Phase 2. |
## Cheat sheet
```bash theme={null}
# Simplest build; finds ./Dockerfile in the context, tags the result.
contree build . --tag myapp:dev
# Out-of-tree Dockerfile.
contree build ./service \
--dockerfile ./service/Dockerfile.prod \
--tag svc:prod
# Override build-time variables.
contree build . \
--build-arg VERSION=2.5 \
--build-arg DEBUG=1
# Force a full rebuild.
contree build . --no-cache --tag myapp:dev
# Raise the per-RUN timeout to 30 minutes.
contree build . --timeout 1800 --tag myapp:dev
# Inspect the build's layer history.
contree session list --filter build:
contree session use build:
contree session show
```
***
You now have a tagged image that came from a `Dockerfile`, a cached
layer history you can branch off, and a feel for which directives
behave and which are parsed-but-skipped. Next, see
[Scripting & Automation](./workflows) for scripting builds into pipelines, or
[build - Build an image from a Dockerfile](../commands/build) for the full reference.
# Configuration & Profiles
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/tutorial/configuration
Profiles let you store credentials for multiple projects or environments
and switch between them. This section walks through setting up profiles,
switching contexts, and understanding how configuration is resolved.
## Creating profiles
Each profile stores a token and API URL for a specific project or
environment. When you first ran `contree auth`, it created a profile
called `default`. Add more with `--profile`:
```bash theme={null}
contree auth --profile=personal
contree auth --profile=sandbox
```
Each command prompts for a token securely (no echo), verifies it against
the API, and writes it to `~/.config/contree/auth.ini`.
The resulting config file looks like this:
```ini theme={null}
[DEFAULT]
profile = default
[profile:default]
token = eyJ...
url = https://api.tokenfactory.nebius.com/sandboxes
type = iam
project = project-id-default
[profile:personal]
token = eyJ...
url = https://api.tokenfactory.nebius.com/sandboxes
type = iam
project = project-id-personal
[profile:sandbox]
token = eyJ...
url = https://api.tokenfactory.nebius.com/sandboxes
type = iam
project = project-id-sandbox
```
Each profile is a `[profile:NAME]` section with `token`, `url`, `type`,
and `project` keys. The `[DEFAULT]` section stores the name of the active
profile.
## Listing profiles
See all saved profiles and which one is active:
```bash theme={null}
contree auth ls
```
The output shows the profile name, URL, a token hash (first 16 chars of
SHA256), active status, and a health check result.
`auth ls` verifies each profile against the API with a 2-second timeout.
Possible status values:
* `ok` — token is valid and has the required sandbox permission
* `timeout` — server did not respond in time
* `error` — bad token or network error
* `offline mode` — you passed `-O` / `--offline`
* `no url` — the profile has no API URL configured (re-run `contree auth`)
* `inactive` — token authenticates, but the configured project does not
grant the sandbox permission this CLI needs
Skip the network check:
```bash theme={null}
contree auth ls -O
```
For automation, use structured output:
```bash theme={null}
contree -o json auth ls
```
## Switching profiles
### Persistent switch
Change the active profile for all future commands:
```bash theme={null}
contree auth switch personal
```
### Per-command override
Use `-p` / `--profile` on any command:
```bash theme={null}
contree -p personal images
contree -p sandbox run -- uname -a
```
### Environment variable
Override for the entire shell session:
```bash theme={null}
export CONTREE_PROFILE=sandbox
contree images # uses sandbox
```
### Inline token
Pass `--token` and `--url` directly:
```bash theme={null}
contree --token=eyJ... --url=https://api.tokenfactory.nebius.com/sandboxes images
```
Avoid `--token` on the command line in production — the token is
visible in process listings and shell history.
## Removing profiles
Delete a profile and its session database:
```bash theme={null}
contree auth remove personal
contree auth rm personal -y # skip confirmation
```
If the removed profile was active, the CLI switches to the first
remaining profile.
## Profiles and sessions
Each profile has its own session database
(`~/.config/contree/sessions-{profile}.db`), so:
* **Same profile, same terminal** — resumes the existing session
* **Different profile, same terminal** — different session, different data
Switching from `default` to `personal` does not affect your `default`
sessions — you can switch back and continue where you left off.
To share a session across profiles (rare), set `CONTREE_SESSION`:
```bash theme={null}
export CONTREE_SESSION=shared-session
```
## Data storage
All data lives in `CONTREE_HOME` (default `$XDG_CONFIG_HOME/contree`,
falling back to `~/.config/contree` when `XDG_CONFIG_HOME` is unset):
| Path | Purpose |
| --------------------------- | ----------------------------------------------------------- |
| `auth.ini` | Profile credentials and settings (created with mode `0600`) |
| `cli.ini` | Optional user-editable defaults for the CLI |
| `cli/sessions/{profile}.db` | Per-profile sessions, history, branches, cache |
| `cli/skills.db` | Installed agent skill registry |
Override with `$CONTREE_HOME`:
```bash theme={null}
export CONTREE_HOME=/custom/path
```
### `cli.ini`
`cli.ini` is meant for hand-editing. Create it yourself; the CLI never
writes to it. Two kinds of sections are supported:
#### `[cli]` section: per-flag defaults
Keys here become argparse defaults. Use the argparse `dest` name (not
the flag name):
| Key | Maps to flag | Notes |
| --------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `log_level` | `--log-level` | One of `debug`, `info`, `warning`, `error`, `critical` |
| `output_format` | `-f` / `--format` | One of the formatter names (`default`, `json`, `json-pretty`, `csv`, `tsv`, `table`) |
| `editor` | `--editor` (file edit) | Fallback when neither `--editor` nor `$EDITOR` is set; if absent the CLI searches `vim` then `nano` on `PATH` and falls back to `vi` |
Example:
```ini theme={null}
[cli]
log_level = debug
output_format = json
editor = nvim
```
Precedence: CLI flag > environment variable > `cli.ini` > built-in
default. A `cli.ini` setting always loses to an explicit flag.
#### `[profile:NAME]` sections: CLI-scoped profiles
`cli.ini` accepts the same `[profile:NAME]` sections as `auth.ini` and
supports the same fields:
| Key | Required | Notes |
| --------- | ----------------------------- | ------------------------ |
| `url` | yes for JWT, optional for IAM | API base URL |
| `type` | optional | `jwt` (default) or `iam` |
| `project` | IAM only | Project ID |
| `token` | optional | API bearer token |
The two files are merged at load time and `auth.ini` wins on conflict.
What `cli.ini` is for: profiles (or any field, including `token`) you
want only the `contree` CLI to see. The CLI merges `cli.ini` with
`auth.ini`. Other contree-related tooling that talks to the API
directly (the SDK, the MCP server) reads only `auth.ini`. Use
`cli.ini` when you need a profile that should be invisible to those
direct-API consumers, or to keep `auth.ini` minimal and shared.
Example, CLI-only profile alongside the shared one:
```ini theme={null}
# ~/.config/contree/auth.ini (read by CLI + SDK + MCP, mode 0600)
[DEFAULT]
profile = default
[profile:default]
url = https://contree.dev
token = eyJhbGciOi...
```
```ini theme={null}
# ~/.config/contree/cli.ini (read only by the CLI)
[profile:cli-sandbox]
url = https://staging.contree.dev
token = eyJhbGciOi...different
```
The active profile is still selected by the `profile` key in
`[DEFAULT]` of `auth.ini` (or by `--profile` / `$CONTREE_PROFILE`).
## Environment variables
Read at runtime by any command:
| Variable | Description |
| ----------------- | --------------------------------------------------------------------------- |
| `CONTREE_HOME` | Data directory (default `$XDG_CONFIG_HOME/contree`, or `~/.config/contree`) |
| `XDG_CONFIG_HOME` | XDG base config dir, used to derive the default `CONTREE_HOME` |
| `CONTREE_PROFILE` | Active profile name (selects which profile commands use) |
| `CONTREE_SESSION` | Explicit session key (overrides auto-generated) |
Read only by `contree auth` (registration-time fallbacks for omitted flags):
| Variable | Used for |
| --------------------------------------- | ----------- |
| `CONTREE_TOKEN` / `NEBIUS_API_KEY` | `--token` |
| `CONTREE_URL` | `--url` |
| `CONTREE_PROJECT` / `NEBIUS_AI_PROJECT` | `--project` |
## Resolution precedence
For token, URL, and project at runtime:
1. CLI flag (`--token`, `--url`, `--project`) — overrides profile for the
current invocation only
2. Saved profile field
3. Built-in default URL for IAM: `https://api.tokenfactory.nebius.com/sandboxes`
Environment variables are not consulted at runtime; to refresh credentials
from environment variables, run `contree auth` (which reads
`CONTREE_TOKEN` / `NEBIUS_API_KEY`, `CONTREE_URL`, and `CONTREE_PROJECT` /
`NEBIUS_AI_PROJECT` as fallbacks for the corresponding flags).
For profiles:
1. `-p` / `--profile` flag
2. `CONTREE_PROFILE` environment variable
3. `profile` key in config `[DEFAULT]` section
4. Falls back to `default`
***
See [auth - Configure credentials and profiles](../commands/auth) for the full auth command reference, or
[Command Reference](../commands/index) for all commands.
# Working with Files
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/tutorial/files
contree-cli lets you inject local files into sandboxes, edit remote files
in-place, and stage changes that automatically apply on the next run.
## Inject a file with `--file`
Use `--file` / `-F` on `contree run` to attach a local file:
```bash theme={null}
contree run --file ./app.py python /app.py
```
```text theme={null}
contree run --file ./app.py python /app.py
```
Flags like `--file` require the explicit `contree run` prefix.
By default the file is placed at the same path inside the sandbox.
Specify a different destination with a colon:
```bash CLI theme={null}
contree run --file ./app.py:/app/main.py python /app/main.py
```
```text Shell theme={null}
contree run --file ./app.py:/app/main.py python /app/main.py
```
### Full `--file` syntax
```
host_path[:instance_path][:uUID][:gGID][:mMODE]
```
* **host\_path** – path to the file on your machine (required)
* **instance\_path** – destination path inside the sandbox (detected by
leading `/`). Defaults to the host path.
* **uUID** – owner UID (prefix `u`). Numeric or name (resolved locally).
* **gGID** – group GID (prefix `g`). Numeric or name (resolved locally).
* **mMODE** – octal permission mode (prefix `m`).
Tagged options (`u`, `g`, `m`) can appear in any order after the host
path. Unspecified values default to the host file’s stat.
```bash theme={null}
# Override only mode
contree run --file ./script.sh:m0755 /script.sh
# All explicit
contree run --file ./app.py:/app.py:u0:g0:m0755 python /app.py
# Named uid/gid (resolved from local system)
contree run --file ./app.py:uroot:groot python /app.py
```
Named uid/gid (e.g. `uroot`) are resolved locally via `pwd`/`grp`
modules. Use numeric IDs if unsure about host/instance mismatch.
### Multiple files
Repeat the `--file` flag:
```bash theme={null}
contree run \
--file ./app.py:/app.py \
--file ./config.yaml:/etc/app/config.yaml \
python /app.py
```
### Directories
`--file` also accepts directories. The entire tree is uploaded recursively:
```bash theme={null}
contree run --file ./src:/app/src -- make -C /app/src
```
Common junk is excluded by default: `.*`, `.git`, `*.pyc`, `__pycache__`,
`.venv`, `.mypy_cache`, `.pytest_cache`, `node_modules`, `dist`, `build`.
Add extra exclusions with `--file-excludes`:
```bash theme={null}
contree run --file ./project:/app --file-excludes '*.log' '*.tmp' -- make -C /app
```
### Upload caching
The CLI keeps a local upload cache keyed by file path, inode, modification
time, and size. Repeated attachments of unchanged files skip both the hash
calculation and the API call. The cache expires after 90 days to account
for server-side file retention.
The server also deduplicates by SHA256 — if the same content was uploaded
from a different session or machine, it is reused without re-uploading.
## Edit remote files
`contree file edit` downloads a file from the session image, opens it
in your `$EDITOR`, and stages the changes as a pending file:
```bash theme={null}
contree file edit /etc/nginx/nginx.conf
```
In the interactive shell, `vim`, `vi`, and `nano` are aliases for
`contree file edit`, using your host `$EDITOR`:
```text theme={null}
vim /etc/nginx/nginx.conf
```
You can also use the full command:
```text theme={null}
contree file edit /etc/nginx/nginx.conf
```
What happens step by step:
1. The file is downloaded from the current session image to a temp file
2. Your editor opens (`$EDITOR`, defaults to `vi`)
3. If you saved changes, the modified file is uploaded and staged as pending
4. If the file is unchanged (same SHA256), nothing is staged
If the file does not exist in the image yet, an empty file is created for
you to fill in.
### Iterate without re-running
You can edit multiple files before running:
```bash CLI theme={null}
contree file edit /etc/nginx/nginx.conf
contree file edit /etc/nginx/sites-enabled/default
contree run nginx -t # test config with both edits applied
```
```text Shell theme={null}
vim /etc/nginx/nginx.conf
vim /etc/nginx/sites-enabled/default
nginx -t
```
Both edits are staged as pending and applied together on the next run.
## Stage local files with `contree file cp`
`contree file cp` copies a local file into the session as a pending file:
```bash CLI theme={null}
contree file cp ./config.yaml /etc/app/config.yaml
```
```text Shell theme={null}
contree file cp ./config.yaml /etc/app/config.yaml
```
This uploads the file and records it as pending – it does not run anything.
The file will be included in the next `contree run` automatically.
### Build up a working environment
```bash CLI theme={null}
contree file cp ./app.py /app/app.py
contree file cp ./requirements.txt /app/requirements.txt
contree file cp ./config.yaml /etc/app/config.yaml
contree run pip install -r /app/requirements.txt
contree run python /app/app.py
```
```text Shell theme={null}
contree file cp ./app.py /app/app.py
contree file cp ./requirements.txt /app/requirements.txt
contree file cp ./config.yaml /etc/app/config.yaml
pip install -r /app/requirements.txt
python /app/app.py
```
The first `run` consumes all three pending files and bakes them into the
new image. The second `run` already has them – no re-upload needed.
## How pending files work
Pending files accumulate until the next `contree run`:
1. Each `file edit` or `file cp` records a pending file in the session
2. When you run `contree run`, all pending files are merged into the payload
3. After the run completes, the new image already contains those files
4. The pending queue is effectively cleared (a new history checkpoint is
created past them)
Explicit `--file` flags on `contree run` take priority over pending files
at the same path.
Pending files are branch-aware – switching branches with
`contree session checkout` changes which pending files are visible.
## Deduplication
Files are uploaded to the API with SHA256 dedup. If the same file content
has already been uploaded (from a previous edit or a different session), it
is reused without re-uploading.
***
You can inject and edit files. Next: [Images & Tags](./images).
# Your First Sandbox
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/tutorial/first-steps
Now that you’re authenticated, let’s spin up a sandbox and explore it.
## Browse images
List available images:
```bash CLI theme={null}
contree images --prefix=ubuntu
```
```text Shell theme={null}
contree images --prefix=ubuntu
```
## Sessions
Every command in ConTree runs inside a **session** — a named workspace
that tracks which image you’re on, your working directory, file uploads,
and full branch/rollback history.
### How sessions are picked
You don’t have to create a session manually. When you run any command,
ConTree auto-generates a session key from your profile, parent process
ID, and terminal (TTY). This means the same terminal window gets the
same session — but **opening a new terminal creates a new session**
because the process ID changes.
There are three ways to control which session is used (in priority order):
1. **`-S` flag** — `contree -S my-session run ...` — explicit, survives terminal restarts
2. **`CONTREE_SESSION` env var** — `export CONTREE_SESSION=my-session` — stable for the shell session
3. **Auto-generated** — derived from profile + PID + TTY (default, tied to current terminal)
### Starting a session
The recommended way is `eval`, which exports the session key so it
survives across commands and is easy to resume later:
```bash theme={null}
eval $(contree use tag:ubuntu:latest)
```
Without `eval`, `contree use` still works within the same terminal —
the auto-generated key is stable as long as the terminal stays open:
```bash theme={null}
contree use tag:ubuntu:latest # sets image
contree run uname -a # same session (same terminal)
```
But if you close the terminal and open a new one, the auto-generated
session key changes. To resume a previous session, use `eval` or `-S`.
### Resuming sessions
List existing sessions and resume one:
```bash theme={null}
contree session list # find the session key
contree -S my-project+a1b2c3d4 use # resume it
```
Or pin a human-readable name from the start:
```bash theme={null}
contree -S build use tag:ubuntu:latest
contree -S build run -- make test
# close terminal, open new one — same session:
contree -S build run -- make deploy
```
For agent workflows or scripts, always use `-S`:
```bash theme={null}
contree -S build-agent use tag:ubuntu:latest
contree -S build-agent run -- make build
contree -S build-agent run -- make test
```
This is the most reliable — no `eval`, no terminal dependency.
Inside `contree shell`, you don’t need `eval` — but the shell still
needs a session. Use `-S` or `CONTREE_SESSION` to pin it:
```bash theme={null}
contree -S my-project shell
```
Without `-S`, the auto-generated session is used (tied to current terminal).
## Run a command
```bash theme={null}
contree run uname -a
```
```text theme={null}
uname -a
```
Bare commands are executed as implicit `run` in the sandbox.
`contree run uname -a` works too – use the explicit form when you need
flags like `-D`, `-e`, or `--file`.
The CLI spawns a sandbox, waits for it to finish, and prints stdout/stderr.
The resulting filesystem becomes the new session image – every non-disposable
run produces a new checkpoint.
The `--` separator is optional. ConTree parses its own flags correctly
regardless. It is a useful convention to visually separate contree options
from the sandbox command:
```bash theme={null}
contree run -D -- apt-get install -y curl
```
Both `contree run uname -a` and `contree run -- uname -a` work the same way.
## Check session status
See what image and session you’re working with:
```bash CLI theme={null}
contree use
```
```text Shell theme={null}
contree use
```
Running `contree use` without arguments prints the current session info.
## Install packages
Commands chain naturally. Each run advances the session image:
```bash theme={null}
contree run apt-get update -qq
contree run apt-get install -y curl
```
```text theme={null}
apt-get update -qq
apt-get install -y curl
```
Or equivalently with the explicit prefix:
```text theme={null}
contree run apt-get update -qq
contree run apt-get install -y curl
```
After these two runs the session image includes `curl`.
## Change working directory
Set a working directory for the session — subsequent commands resolve
relative paths against it:
```bash theme={null}
contree cd /app
contree run -- ls # lists /app
contree cat README.md # reads /app/README.md
contree cd # reset to sandbox default
```
## Inspect the filesystem
List files and read content without spawning a new sandbox:
```bash theme={null}
contree ls /usr/bin
contree cat /etc/os-release
```
```text theme={null}
ls /usr/bin
cat /etc/os-release
```
`ls` and `cat` are aliases for the contree API commands (no sandbox spawned).
To run the actual instance commands instead, use the explicit prefix:
```text theme={null}
contree run ls /usr/bin
contree run cat /etc/os-release
```
## Download a file
Copy a file from the sandbox to your local machine:
```bash theme={null}
contree cp /etc/os-release ./os-release.txt
```
```text theme={null}
contree cp /etc/os-release ./os-release.txt
```
There is no short alias for `cp` – use the full `contree cp` prefix.
## Disposable mode
Use `-D` / `--disposable` when you want to run a command without advancing the
session image. Changes are discarded after execution:
```bash theme={null}
contree run -D -- rm -rf /important
```
```text theme={null}
contree run -D -- rm -rf /important
```
Flags like `-D` require the explicit `contree run` prefix.
The session image stays exactly where it was before this run.
***
Your session tracks every command. Next: [Interactive Shell](./shell).
# Images & Tags
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/tutorial/images
Images are filesystem snapshots. Every non-disposable `contree run`
produces a new image. Tags give images human-readable names so you
can find and reuse them later.
All data — images, operations, and uploaded files — is scoped to a
**Project**. Multiple tokens can access the same project and share
its data. Different projects have separate scopes — nothing is
visible across project boundaries.
## Listing images
Show tagged images:
```bash theme={null}
contree images
```
Filter by tag prefix:
```bash theme={null}
contree images --prefix=ubuntu
contree images --prefix=my-app
```
Include untagged images:
```bash theme={null}
contree images -a
```
Filter by time:
```bash theme={null}
contree images --since 1d # last 24 hours
contree images --since 2025-04-01
```
## Tagging images
With one argument, tags the current session image. With two, the first
is the image reference:
```bash theme={null}
contree tag my-app:v1.0 # current session image
contree tag UUID my-app:v1.0 # specific image by UUID
contree tag tag:alpine:latest my-alpine # re-tag by reference
```
The CLI resolves tag references to UUIDs automatically — you don’t
need to look up the UUID first.
Tags follow a free-form `name:version` convention. Common patterns:
```bash theme={null}
contree tag UUID my-app:latest
contree tag UUID my-app:v2.0
contree tag UUID common/python-ml/python:3.11-slim
```
Remove a tag:
```bash theme={null}
contree tag -U UUID my-app:v1.0
```
### Tag rules
* Tags are scoped to your Project (API token)
* Each image can have multiple tags
* Tags are unique — assigning an existing tag to a different image **moves** it
* Allowed characters: `a-z`, `0-9`, `_`, `-`, with `:`, `/`, `.` as separators
* Max length: 256 characters
* Case-sensitive (lowercase recommended)
### Shadow behavior
Public images (like `ubuntu:latest`) have their own tags. When you assign
the same tag to your own image, the public image is still accessible by
UUID but its tag becomes shadowed. Removing your tag restores the public
one.
## Using tags
Use `tag:NAME` anywhere an image UUID is expected:
```bash theme={null}
contree use tag:my-app:latest
```
If both you and a public image share a tag, your image wins.
## Importing images
Pull images from container registries (Docker Hub, GHCR, etc.):
```bash theme={null}
contree images import ubuntu:latest
contree images import --timeout 600 ubuntu:latest
contree images import python:3.11-slim
contree images import ghcr.io/org/repo:tag
```
Import is asynchronous — the CLI polls until the operation completes.
Press Ctrl+C to cancel.
Import multiple at once:
```bash theme={null}
contree images import ubuntu:latest python:3.11-slim node:20-slim
```
### Private registries
Authenticate with `--username`:
```bash theme={null}
contree images import --username=user registry.example.com/image:tag
```
The password is prompted securely if `--username` is provided.
Credentials are used only for the import operation and discarded
immediately after — the server does not store them.
## Reusing images across sessions
A common workflow is to prepare a base environment, tag it, and
reuse it in future sessions:
```bash theme={null}
# Session 1: build the environment
contree use tag:ubuntu:latest
contree run apt-get update -qq
contree run apt-get install -y python3 python3-pip build-essential
contree tag UUID python-dev:latest
# Session 2 (days later): start from the prepared image
contree use tag:python-dev:latest
contree run pip install -r requirements.txt
```
Search for existing tagged images before rebuilding:
```bash theme={null}
contree images --prefix=python-dev
```
***
Images are your checkpoints. Next: [Building from a Dockerfile](./build).
# Tutorial
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/tutorial/index
Learn contree-cli by building a real workflow – from zero to automated
scripting in nine short sections.
## What you’ll build
By the end of this tutorial you will:
* Spin up sandboxes from images and run arbitrary commands
* Track sandbox state through sessions with branching and rollback
* Inject local files, edit remote configs, and tag working images
* Script everything with JSON output, detached runs, and operation monitoring
## Before you start
You need two things:
* **Python 3.10+** installed on your machine
* **A ConTree API token** — get one from your [project dashboard](https://contree.dev)
## The path
Install contree-cli, save your API token, and set up named profiles for
different environments.
Browse images, run commands, inspect the filesystem, and download files.
Understand how each run creates a new checkpoint.
Use the REPL for rapid iteration: tab completion for paths, images, and
branches, command aliases, and persistent history.
Branch off to experiment, roll back mistakes, share sessions across
terminals, and start fresh when needed.
Inject local code into sandboxes, edit remote files in-place, and stage
changes that auto-attach on the next run.
Tag working images for reuse, import from registries, and search by
prefix. Build reusable base environments.
Turn a `Dockerfile` into a tagged ConTree image. Layer caching,
build args, `.dockerignore`, and URL streaming for `ADD`.
Shell mode, shebang scripts, detached runs, operation monitoring, and
machine-readable output formats for pipelines.
Create and switch between profiles for different projects, understand
how profiles affect sessions, and configure environment variables.
## Quick taste
If you just want to see contree-cli in action before diving in:
```bash theme={null}
# install
git clone https://github.com/nebius/contree-cli.git
cd contree-cli && uv sync
# authenticate (token prompted securely)
contree auth
# start a session and run a command
eval $(contree use tag:ubuntu:latest)
contree run uname -a
# inspect the result
contree ls /
contree cat /etc/os-release
```
```bash theme={null}
# install and authenticate first (see CLI tab)
contree auth
# start the interactive shell
contree shell
```
Once inside the shell:
```text theme={null}
contree use tag:ubuntu:latest
uname -a
ls /
cat /etc/os-release
```
Ready? Start with [Install & Authenticate](./installation).
# Install & Authenticate
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/tutorial/installation
## Requirements
* Python 3.10 or later
* The `contree-client` library (installed automatically)
## Install
```bash uv (recommended) theme={null}
uv tool install contree-cli
```
```bash pip theme={null}
pip install contree-cli
```
```bash pipx theme={null}
pipx install contree-cli
```
```bash From source theme={null}
git clone https://github.com/nebius/contree-cli.git
cd contree-cli
uv sync
```
Verify the installation:
```bash theme={null}
contree --help
```
### Development setup
To work on contree-cli itself:
```bash theme={null}
git clone https://github.com/nebius/contree-cli.git
cd contree-cli
uv sync --group dev
make check # lint + type check
make tests # lint + type check + pytest
```
## Authenticate
All ConTree API calls require a bearer token and a project ID.
### Save credentials
Get an API token and project ID from your ConTree project, then save them:
```bash theme={null}
contree auth
```
You will be prompted to enter:
1. **Token** — entered securely (no echo)
2. **Project ID** — your project identifier
The CLI verifies the token with the API and writes credentials to
`~/.config/contree/auth.ini`. If a profile already exists you will be
prompted to confirm; use `-y` to skip the prompt.
Resolution order for each field during `contree auth` (first match wins):
1. CLI flag (`--token`, `--url`, `--project`)
2. Environment variables, in order:
* token: `CONTREE_TOKEN`, then `NEBIUS_API_KEY`
* URL: `CONTREE_URL`
* project: `CONTREE_PROJECT`, then `NEBIUS_AI_PROJECT`
3. Interactive prompt
So if these variables are already in your environment and no flags
are passed, `contree auth` picks them up automatically, no interactive
prompts needed:
```bash theme={null}
export NEBIUS_API_KEY=eyJ...
export NEBIUS_AI_PROJECT=your-project-id
contree auth -y # fully non-interactive
```
Avoid `contree auth --token=eyJ...` — the token is visible in process
listings and shell history. Omit `--token` to use the secure prompt.
### Named profiles
Store multiple tokens for different projects or environments:
```bash theme={null}
contree auth --profile=personal
contree auth --profile=sandbox
```
List all profiles:
```bash theme={null}
contree auth ls
```
Switch the active profile permanently:
```bash theme={null}
contree auth switch personal
```
Or use a profile temporarily (single session, no config change):
```bash theme={null}
export CONTREE_PROFILE=personal
contree images # uses personal
```
### Token from environment
`CONTREE_TOKEN` and `NEBIUS_API_KEY` are read **only** by `contree auth`
during profile registration; runtime commands always read credentials
from the saved profile. To bootstrap a profile entirely from environment
variables, run `auth` non-interactively:
```bash theme={null}
export CONTREE_TOKEN=eyJ...
export CONTREE_URL=https://api.tokenfactory.nebius.com/sandboxes
contree auth -y --type jwt # one-shot setup, no prompts
contree images
```
### Inline token
Pass `--token` to any command to override the saved profile for a single
invocation:
```bash theme={null}
contree --token=eyJ... images
```
***
You’re authenticated. Next: [Your First Sandbox](./first-steps).
# Sessions, Branches & Rollback
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/tutorial/sessions
Sessions track the current image and its history as you run commands. Every
non-disposable `contree run` produces a new image, and the session records
the chain. Sessions also support **branching** and **rollback** for
experimentation.
## Session key
Every session is identified by a **session key** – an arbitrary string.
The CLI computes one automatically so that each terminal window gets its
own session without any extra setup.
The auto-generated key is a deterministic UUID5 derived from three values:
| Component | Source | Purpose |
| --------- | ------------------------------------ | ------------------------------- |
| `profile` | Active config profile name | Isolates sessions per profile |
| `ppid` | Parent process ID (`os.getppid()`) | The shell that launched the CLI |
| `tty` | TTY device of stdin (`os.ttyname()`) | Distinguishes terminal windows |
**In practice this means:**
* Open a new terminal tab – new `ppid` + `tty` – **new session**.
* Run `contree` commands in the same terminal – same `ppid` + `tty` –
**same session** (resumes where you left off).
* Switch profiles – different `profile` – **new session**.
## Viewing session state
```bash CLI theme={null}
contree session # show current session info
contree session list # list all sessions
contree session show # show full history DAG
```
```text Shell theme={null}
contree session
contree session list
contree session show
```
## Branching
Create a branch to experiment without affecting the main line:
```bash CLI theme={null}
contree session branch experiment
contree session checkout experiment
contree run apt-get install -y curl
```
```text Shell theme={null}
contree session branch experiment
contree session checkout experiment
apt-get install -y curl
```
Not happy? Switch back:
```bash CLI theme={null}
contree session checkout main
```
```text Shell theme={null}
contree session checkout main
```
Branches share history entries – creating a branch just creates a new
pointer at the current position.
Create a branch from another branch:
```bash CLI theme={null}
contree session branch hotfix --from main
```
```text Shell theme={null}
contree session branch hotfix --from main
```
List branches (`*` marks the active one):
```bash CLI theme={null}
contree session branch
```
```text Shell theme={null}
contree session branch
```
## Rollback
Move the branch pointer in the history chain. The argument distinguishes
absolute jumps from relative navigation:
| Argument | Meaning |
| ------------------- | -------------------------------------------------------------------- |
| *(none)* | Back one entry (default) |
| `-- -N` | Back N entries (the `--` stops argparse from parsing `-N` as a flag) |
| `+N` | Forward N entries |
| `N` (bare positive) | **Absolute** jump to history id `N` |
```bash CLI theme={null}
contree session rollback # back one entry
contree session rollback -- -3 # back three entries
contree session rollback +1 # forward one entry
contree session rollback 42 # absolute jump to history id 42
```
```text Shell theme={null}
contree session rollback
contree session rollback -- -3
contree session rollback +1
contree session rollback 42
```
A bare positive number is an **absolute** history id, not “back N steps”.
Use `--` followed by a negative number for relative back-navigation.
Inspect with `contree session show` first to avoid surprise jumps.
The history entries still exist and can be recovered by creating a branch at
a specific point.
## Starting a fresh session
Because the auto-generated key is deterministic, the same terminal always
resumes the same session. Use `--new` (`-N`) to start a fresh session:
```bash theme={null}
# bash / zsh
eval $(contree use -N tag:python:3.11-slim)
# fish
eval (contree use -N tag:python:3.11-slim)
```
Without `eval`, the new session is **not active** until you export the
printed variable into your shell. You can also copy-paste the `export`
(or `set -gx`) line that `contree use` prints.
```text theme={null}
contree use -N tag:python:3.11-slim
```
Inside the interactive shell, no `eval` is needed – the new session is
activated automatically.
You can also set `CONTREE_SESSION` to any string:
```bash theme={null}
export CONTREE_SESSION=tutorial
contree use tag:python:3.11-slim
```
Unset it to go back to the automatic key:
```bash theme={null}
unset CONTREE_SESSION
```
## Sharing a session across terminals
`contree use` prints the session key. Export it in another terminal to
attach to the same session:
```bash theme={null}
# Terminal 1
contree use tag:ubuntu:latest
# output: export CONTREE_SESSION=
# Terminal 2 -- paste the line above
export CONTREE_SESSION=
contree run ls / # operates on the same session
```
This pattern is CLI-only. The interactive shell manages sessions internally
and does not require manual session key export.
## Storage
Session data is stored in a per-profile SQLite database at
`~/.config/contree/sessions-{profile}.db`. Override the data
directory with `CONTREE_HOME`:
```bash theme={null}
export CONTREE_HOME=/tmp/contree-data
```
***
You can experiment freely with branches. Next: [Working with Files](./files).
# Interactive Shell
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/tutorial/shell
`contree shell` starts a REPL that combines management commands and sandbox
execution in a single session. It is the fastest way to explore images, run
commands, edit files, and manage branches – all without leaving the prompt.
## Starting the shell
The shell needs a session, just like any other command. You can pin one
explicitly or let it auto-generate:
```bash theme={null}
contree -S my-project shell # explicit session name
CONTREE_SESSION=my-project contree shell # same via env var
contree shell # auto-generated (tied to terminal)
```
The shell prints a coloured prompt showing the current working directory:
```text theme={null}
contree interactive shell (type 'help' for commands, Ctrl-D to exit)
contree:/>
```
If the session already has an image (from a previous `contree use`), the
shell resumes it. Otherwise, set one first:
```text theme={null}
contree:/> contree use tag:ubuntu:latest
```
## Running commands
Type any command and it runs inside the sandbox:
```text theme={null}
contree:/> apt-get update -qq
contree:/> apt-get install -y curl
contree:/> curl https://example.com
```
Each command is an implicit `contree run` with `shell=True`. The whole
input line is forwarded verbatim to the remote `sh -c`, so pipes,
redirects, `;`, `&&`, and `||` are interpreted by the sandbox shell
exactly as typed:
```text theme={null}
contree:/> echo hello && ls / | head -5
contree:/> mount | grep cgroup
contree:/> echo 1 ; echo 2
contree:/> uname -a > /tmp/info.txt
```
Quoting from your local prompt is also preserved through to the remote
shell:
```text theme={null}
contree:/> python3 -c "print('hello world')"
```
You can also use the explicit form, which is equivalent:
```text theme={null}
contree:/> contree run apt-get install -y curl
```
The explicit `contree run` prefix is required when you need flags like
`-D` (disposable), `-e` (env), `-t` (timeout), `--file`, or `-d` (detach):
```text theme={null}
contree:/> contree run -D -- rm -rf /tmp/*
contree:/> contree run -e DEBUG=1 -- ./app
contree:/> contree run -d -- long-running-task
```
## Tab completion
The shell supports context-aware tab completion for nearly everything.
Press Tab at any point to see available completions.
### What completes
**Commands and subcommands** – type `contree` then Tab to see all
available subcommands. Type a partial name and Tab completes it:
```text theme={null}
contree:/> contree ses
contree:/> contree session
branch checkout list rollback show use
```
**Flags** – type `-` or `--` after a command and Tab shows available
flags:
```text theme={null}
contree:/> contree run --
--cwd --detach --disposable --env --file --hostname ...
```
**Sandbox paths** – any command that takes a file path completes
against the actual sandbox filesystem. The shell queries the image
via the inspect API and caches the results:
```text theme={null}
contree:/> ls /etc/
apt/ bash.bashrc default/ hostname nginx/ passwd ...
contree:/> cat /etc/os-
contree:/> cat /etc/os-release
contree:/> vim /etc/nginx/
nginx.conf sites-enabled/
```
**Directory-only paths** – `cd` completes only directories:
```text theme={null}
contree:/> cd /us
contree:/> cd /usr/
bin/ include/ lib/ local/ sbin/ share/
```
**Images** – `contree use` and `contree tag` complete image references.
Type `tag:` to filter by tag names, or start typing a UUID:
```text theme={null}
contree:/> contree use tag:
tag:ubuntu:latest tag:python:3.11-slim tag:common/rust/ubuntu:noble ...
contree:/> contree use tag:py
contree:/> contree use tag:python:3.11-slim
```
**Operations** – `contree show` and `contree kill` complete operation
UUIDs:
```text theme={null}
contree:/> contree show
a1b2c3d4-... e5f6a7b8-...
```
**Branches and sessions** – session management subcommands complete
branch names and session keys:
```text theme={null}
contree:/> contree session checkout
main experiment hotfix
contree:/> contree session use
abc123_def456 tutorial ci-build-42
```
**Help topics** – `help` completes all command and alias names:
```text theme={null}
contree:/> help
cat cd contree exit help history ls nano pwd quit vim ...
```
### What does not complete
**Bare commands** (implicit `run`) do not have tab completion. The shell
does not know what executables exist inside the sandbox, so typing a
bare command name and pressing Tab will not offer suggestions:
```text theme={null}
contree:/> apt-g # no completion
contree:/> pyth # no completion
```
However, paths starting with `/` do complete even in bare command context:
```text theme={null}
contree:/> python /app/
main.py utils.py config.yaml
```
## Aliases
The shell intercepts several bare command names for convenience:
### `ls` and `cat`
Bare `ls` and `cat` are forwarded as contree API commands – they inspect
the sandbox filesystem without spawning a new instance:
```text theme={null}
contree:/> ls /etc
contree:/> cat /etc/os-release
```
This is equivalent to `contree ls` and `contree cat`. To run the actual
`ls` or `cat` binary inside the sandbox instead, use the explicit prefix:
```text theme={null}
contree:/> contree run ls -la /etc
contree:/> contree run cat -n /etc/os-release
```
When pending files exist (from `contree file edit` or `contree file cp`),
`ls` and `cat` automatically fall back to running inside the sandbox so
the pending files are visible.
The same fallback happens when arguments contain flags (`-l`) or glob
characters (`*`, `?`, `[`).
### `vim`, `vi`, `nvim`, `nano`
Editor names open `contree file edit` with the corresponding host editor:
```text theme={null}
contree:/> vim /etc/nginx/nginx.conf
```
This downloads the file, opens it in vim on your machine, and stages any
changes as a pending file for the next run.
## Builtins
### `cd`
Change the working directory for subsequent commands:
```text theme={null}
contree:/> cd /app
contree:/app> python main.py
contree:/app> cd - # go back to previous directory
contree:/>
```
`cd` without arguments resets to the sandbox’s default working directory.
`cd` does not validate that the path exists in the sandbox. Errors
surface only when the next command uses the invalid path.
### `pwd`
Print the current working directory:
```text theme={null}
contree:/app> pwd
/app
```
### `history`
Show command history for the current session, optionally filtered by a
case-insensitive substring:
```text theme={null}
contree:/> history # show all entries
contree:/> history apt # only lines containing "apt"
contree:/> history 'contree ' # quoted match (note trailing space)
```
History is persisted in SQLite per session (up to 10,000 lines) and
restored when you re-enter the shell. Search is scoped to the current
session key; different sessions have isolated history.
### `help`
Show general shell help, or help for a specific command or builtin:
```text theme={null}
contree:/> help
contree:/> help cd
contree:/> help run
```
Bare `help` prints an overview of builtins, aliases, line continuation,
and tab completion. `help ` shows detailed help for a builtin,
alias, or contree command.
### `clear`
Clear the terminal screen:
```text theme={null}
contree:/> clear
```
### `timeout`
Run a command with a server-enforced operation timeout. Mirrors the GNU
`timeout` convention but sets `payload.timeout` on the API request instead
of spawning a local wrapper inside the sandbox:
```text theme={null}
contree:/> timeout 30 apk add gcc
contree:/> timeout 5m make build
contree:/> timeout 1h python long_train.py
```
`DURATION` accepts a bare integer or decimal (seconds by default) and
the suffixes `s`, `m`, `h`, `d`. When the value cannot be parsed, the
shell forwards the line untouched so the in-image `timeout` binary still
handles advanced flags like `--kill-after` or `-s SIGTERM`.
When the limit fires, the API returns `state.timed_out=true` (status may
still be `SUCCESS` with `signal=9`), and the shell logs:
```text theme={null}
WARNING: Operation timed out after 30s
```
### `--format` / `-f`
Change the output format mid-session, or show the current format:
```text theme={null}
contree:/> --format json # switch to JSON output
contree:/> -o table # switch to table output
contree:/> --format # show current format name
```
## Workflow example
A typical shell session putting it all together:
```text theme={null}
contree:/> contree use tag:ubuntu:latest
contree:/> apt-get update -qq
contree:/> apt-get install -y python3 python3-pip
contree:/> contree file cp ./app.py /app/app.py
contree:/> contree file cp ./requirements.txt /app/requirements.txt
contree:/> cd /app
contree:/app> pip install -r requirements.txt
contree:/app> python3 app.py
contree:/app> vim app.py # edit and re-run
contree:/app> python3 app.py
contree:/app> contree session branch stable
contree:/app> contree tag UUID my-app:v1
```
## Limitations
* **Output format is fixed** – the `--format` flag is set at `contree shell`
launch. To use JSON output: `contree -o json shell`.
* **No local pipes or redirects** – `|`, `>`, `<` are sent to the sandbox,
not interpreted locally.
* **No job control** – no `&`, `bg`, `fg`, or Ctrl-Z. Use `contree run -d`
for detached execution.
* **Bare commands use defaults** – you cannot pass `--env`, `--file`, or
`--disposable` without the explicit `contree run` prefix. The operation
timeout has a shell shortcut: `timeout DURATION CMD...` (see above).
* **No `~` or glob expansion** – these tokens are passed as-is to the
sandbox.
* **Image list cache** – newly created images during a session won’t appear
in tab completion until the shell is restarted. Path completions are
cached per image and refresh when the session image advances.
***
The shell is the fastest way to iterate. Next: [Sessions, Branches & Rollback](./sessions).
# Scripting & Automation
Source: https://docs.tokenfactory.nebius.com/sandboxes/cli/tutorial/workflows
contree-cli is designed for scripting. Exit codes propagate, output formats
are machine-readable, and shebang mode lets you write executable sandbox
scripts.
## Shebang scripts
Shebang scripts are a CLI-only feature. They are not available in the
interactive shell.
Any file with a `contree run -I` shebang runs inside a sandbox:
```bash theme={null}
#!/usr/bin/env -S contree run -I
echo "Hello from a ConTree sandbox"
uname -a
```
Save it, `chmod +x`, and run it directly:
```bash theme={null}
chmod +x hello.sh
./hello.sh
```
The `-I` (interpreter) flag reads the script, strips the shebang line, and
sends the body as stdin to `/bin/sh -s` inside the sandbox.
### Combining flags
Shebang flags stack. A disposable run with a 10-second timeout:
```bash theme={null}
#!/usr/bin/env -S contree run -I -D -t 10
apt-get update -qq
apt-get install -y curl
curl https://example.com
```
Since `-D` is set, the session image is not advanced – the script runs in
a throwaway sandbox.
### Passing arguments
Extra arguments after the script name are forwarded to the shell:
```bash theme={null}
#!/usr/bin/env -S contree run -I
echo "arg1=$1 arg2=$2"
```
```bash theme={null}
./script.sh foo bar
# arg1=foo arg2=bar
```
The `-S` flag on `/usr/bin/env` is required because the `contree` entry point
is a Python script. Without `-S`, the kernel sees a nested shebang
(script -> script -> binary) and returns ENOEXEC. Using `/usr/bin/env -S`
(a real binary) splits the argument string and avoids this.
## Execution modes
### Direct command
The default mode. Each positional argument becomes a separate argv entry:
```bash CLI theme={null}
contree run uname -a
```
```text Shell theme={null}
uname -a
```
### Shell mode
`-s` joins all arguments into a single shell expression:
```bash theme={null}
contree run -s -- 'echo hello && ls /'
```
```text theme={null}
echo hello && ls /
```
Bare commands in the shell always use shell mode.
Useful when you need pipes, redirects, or `&&` chains.
### Piped stdin
Piped stdin is a CLI-only feature. It is not available in the interactive
shell.
When stdin is not a TTY, it is read, base64-encoded, and sent to the sandbox:
```bash theme={null}
echo 'uname -a' | contree run /bin/sh
```
```bash theme={null}
cat deploy.sh | contree run /bin/sh
```
### Detached mode
`-d` spawns the operation and exits immediately, printing the operation UUID:
```bash theme={null}
contree run -d -- long-running-task
```
Check on it later:
```bash theme={null}
contree show UUID
```
```text theme={null}
contree run -d -- long-running-task
contree show UUID
```
Flags like `-d` require the explicit `contree run` prefix.
## Exit codes
Exit code propagation is a CLI-only feature useful for scripting. The
interactive shell does not expose sandbox exit codes.
The sandbox exit code propagates to the CLI process:
```bash theme={null}
contree run -- /bin/sh -c 'exit 42'
echo $? # 42
```
This means `contree run` works naturally in `if`, `&&`, `||`, and `set -e`
scripts:
```bash theme={null}
set -e
contree run -- make test # script aborts if tests fail
contree run -- make install
```
If the operation fails at the platform level (timeout, cancelled), the CLI
exits with code 1.
## Environment variables
Pass environment variables into the sandbox with `-e`:
```bash theme={null}
contree run -e DEBUG=1 -e DB_HOST=postgres -- ./app
```
```text theme={null}
contree run -e DEBUG=1 -e DB_HOST=postgres -- ./app
```
Flags like `-e` require the explicit `contree run` prefix.
The flag is repeatable. Format is `KEY=VALUE`.
## Output truncation
By default, stdout/stderr is capped at 64 KiB in the API response. Override
with `-T`:
```bash CLI theme={null}
contree run -T 1048576 -- ./generate-big-output.sh
```
```text Shell theme={null}
contree run -T 1048576 -- ./generate-big-output.sh
```
## Monitor operations
List running and recent operations:
```bash CLI theme={null}
contree ps # active operations only
contree ps -a # all (including completed)
contree ps -q # UUIDs only, one per line
```
```text Shell theme={null}
contree ps
contree ps -a
contree ps -q
```
Show the full result of a specific operation:
```bash CLI theme={null}
contree show UUID
```
```text Shell theme={null}
contree show UUID
```
Cancel an operation:
```bash CLI theme={null}
contree kill UUID
contree kill --all
```
```text Shell theme={null}
contree kill UUID
contree kill --all
```
### Fan-out + wait
When several independent steps can run at the same time, spawn each
one detached and join them with `contree op wait` (alias `contree operation wait`). The wait command polls the API and prints one row
per operation as soon as it reaches a terminal status, with columns
`uuid`, `status`, `exit_code`, `timed_out`, `duration`, and any other
scalar field the API returns. The `status` column is the server’s
verdict (did the API run the job?) and is reported verbatim; the
sandbox process’s own exit code is in the separate `exit_code` column.
The CLI exit status is `1` when any op finished non-`SUCCESS`, or the
actual `exit_code` when a `SUCCESS` op exited non-zero, so the wait
still composes naturally with `&&`.
`op wait` is a **pure observer** — it polls completion status but
**does not touch local session state**. That makes the pattern most
natural with `--disposable` (no image to track). For non-disposable
fan-out, the result images live only on the server; the
`detached-` branches created at spawn time still point at
the **starting** image and never get moved. See the non-disposable
recovery example below.
The preferred shape — disposable runs, parallel independent checks.
The global `-o json` must come BEFORE the subcommand so that `jq`
gets JSON; the default `run -d` formatter is plain.
```bash theme={null}
# Three parallel test suites, results discarded after the runs
A=$(contree -o json run -d --disposable -- pytest tests/a | jq -r .uuid)
B=$(contree -o json run -d --disposable -- pytest tests/b | jq -r .uuid)
C=$(contree -o json run -d --disposable -- pytest tests/c | jq -r .uuid)
# Block until each one finishes (or 60 s elapses, whichever comes first)
contree op wait "$A" "$B" "$C"
# Inspect stdout/stderr per leg
contree op show "$A" "$B" "$C"
```
Non-disposable fan-out works too, but you have to recover the result
images yourself — `op wait` will not bind them into the session:
```bash theme={null}
A=$(contree -o json run -d -- apt-get install -y curl | jq -r .uuid)
B=$(contree -o json run -d -- apt-get install -y wget | jq -r .uuid)
contree op wait "$A" "$B"
# Pull the winning leg's image out of the operation result and
# attach it to the active session.
IMG_A=$(contree -o json op show "$A" | jq -r .image)
contree use "$IMG_A"
# Or tag it for reuse later.
contree tag "$IMG_A" feature/curl-tools
```
After fan-out + wait the session retains a `detached-`
branch per spawn. They all point at the image that existed when the
fan-out started, so they are mostly cosmetic — feel free to delete
them with `contree session branch --prune` when you no longer need
them.
Useful flags:
* `--timeout SECONDS` — cap on the wait (default 60). If the deadline
hits before every operation reaches a terminal status, `op wait`
emits one extra row per unfinished op with `timed_out=true` and the
operation’s last observed status (e.g. `EXECUTING`), then exits
with status `1`.
* `--all` — wait for every currently active operation in the project,
not just the ones you passed.
```bash theme={null}
# Block on every active op, up to 5 minutes
contree op wait --all --timeout 300
```
`--all` is **project-scoped**. If multiple agents or shell sessions
share the same project, `op wait --all` will block on every active
operation across all of them — not just the ones you launched. For
multi-agent or multi-shell setups prefer the explicit
`op wait UUID1 UUID2 ...` form with the UUIDs you actually own.
`op wait` exits non-zero whenever any operation finished with a
non-`SUCCESS` status (so it composes naturally with shell `&&`
chains), even when no `--timeout` was hit.
```bash theme={null}
# Run fan-out + tests; bail if any leg failed
contree op wait "$A" "$B" "$C" && echo "all green" || echo "some failed"
```
### Scripting patterns
Shell piping and command substitution are CLI-only features. These patterns
are not available in the interactive shell.
Combine `-q` with other tools:
```bash theme={null}
# Show results of all active operations
contree ps -q | xargs -I {} contree show {}
# Kill all running operations
contree ps -q | xargs -I {} contree kill {}
# Launch detached, capture UUID
OP=$(contree run -d -- sleep 3600)
# ... do other work ...
contree show "$OP"
```
## Output formats
The `--format` flag is global and set at CLI launch time. In the interactive
shell, the format is fixed for the entire session and cannot be changed
mid-session.
Use `-f` / `--format` to control output. The flag is global and goes
before the subcommand:
`default`
: Table-like output optimized for human reading. Some commands (like
`run`) use a custom default that prints only stdout/stderr.
`table`
: Aligned columns with headers. Identical to `default` for most commands.
`csv`
: Comma-separated values with a header row. Useful for spreadsheet import
or `cut`/`awk` processing.
`tsv`
: Tab-separated values with a header row. Works well with `column -t`.
`json`
: One JSON object per line (JSONL/NDJSON). Each output row is a separate
JSON object. Suitable for `jq` processing.
`json-pretty`
: All rows collected into a single pretty-printed JSON array. Output is
flushed at the end.
### Examples
```bash theme={null}
# Pipe JSON to jq
contree -o json ps | jq '.uuid'
# CSV for scripting
contree -o csv images > images.csv
# Tab-separated for column alignment
contree -o tsv ps | column -t
# Get image UUID from tag
contree -o json images --prefix=ubuntu | jq -r '.uuid'
```
### Streaming behavior
`json` and `json-pretty` formatters support streaming output from
commands like `run` and `show` – stdout/stderr are included in the
JSON payload.
`csv`, `tsv`, and `table` formatters do not include stdout/stderr
from sandbox execution. Use `default` or `json` formats to see
sandbox output.
## Session management in scripts
Script-level session management with `eval` and `CONTREE_SESSION` is a
CLI-only pattern. The interactive shell manages sessions automatically.
The `eval $(contree use ...)` pattern exports the session key into your
shell. In scripts, set `CONTREE_SESSION` explicitly to control which
session you operate on:
```bash theme={null}
#!/bin/bash
export CONTREE_SESSION=ci-build-$$
contree use tag:ubuntu:latest
contree run apt-get update -qq
contree run apt-get install -y build-essential
contree run --file ./src:/src make -C /src test
```
Using `$$` (PID) or a fixed name gives you a predictable, isolated session
per script run.
***
You now know the full CLI. Next: [Configuration & Profiles](./configuration).
# Cheatsheet
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/cheatsheet
Quick reference card for AI agents using Contree MCP.
## Tools at a Glance
| Tool | When to Use | Cost |
| ------------------ | ------------------------------- | ---- |
| `list_images` | Before importing anything | Free |
| `import_image` | Need new base image | VM |
| `rsync` | Local files needed in container | Free |
| `run` | Execute code | VM |
| `upload` | Single file to container | Free |
| `download` | Extract file from container | Free |
| `get_image` | Check if image exists | Free |
| `set_tag` | Name frequently-used images | Free |
| `list_files` | Explore container filesystem | Free |
| `read_file` | Read file from container | Free |
| `get_operation` | Poll async operation | Free |
| `list_operations` | Find running operations | Free |
| `wait_operations` | Wait for multiple ops | Free |
| `cancel_operation` | Stop stuck operation | Free |
| `get_guide` | Get documentation sections | Free |
## Common Workflows
### Run Python Code
```
1. list_images(tag_prefix="python") → Check existing
2. import_image(docker://python:3.11) → If needed
3. rsync(source="/project", dest="/app") → Sync files
4. run(cmd, image, ds_id) → Execute
```
### Install Dependencies
```
1. run(pip install ..., disposable=false) → Save image
2. Use result_image for subsequent commands
```
### Parallel Execution
```
1. run(cmd1, wait=false) → op-1
2. run(cmd2, wait=false) → op-2
3. wait_operations([op-1, op-2]) → Get both results
```
### Rollback
```
1. Save last good image UUID
2. If something breaks, use the saved UUID
3. No cleanup needed—images are immutable
```
### Inspect Container (No VM)
```
1. list_files(image, path="/etc") → List directory
2. read_file(image, path="/etc/os-release") → Read file
```
## Decision Quick Guide
| Situation | Action |
| -------------------------- | ------------------------------------ |
| First time using an image | `list_images` first |
| Running existing code | `rsync` + `run` |
| Installing packages | `run` with `disposable=false` |
| Multiple independent tasks | Use `wait=false` + `wait_operations` |
| Long-running command | Increase `timeout` |
| Large output expected | Increase `truncate_output_at` |
| Need to save state | `disposable=false` |
| One-off experiment | `disposable=true` (default) |
## Parameters Quick Reference
### run
```json theme={null}
{
"command": "...", // Required
"image": "uuid", // Required
"directory_state_id": "ds-...", // From rsync
"disposable": true, // Discard changes (default)
"timeout": 30, // Seconds
"env": {"KEY": "value"}, // Environment
"wait": true // Sync execution (default)
}
```
### rsync
```json theme={null}
{
"source": "/local/path", // Required
"destination": "/container/path", // Required
"exclude": ["__pycache__", ".git", ".venv", "node_modules"]
}
```
### import\_image
```json theme={null}
{
"registry_url": "docker://image:tag", // Required
"tag": "my-tag", // Optional, for frequent reuse
"wait": true // Sync (default)
}
```
## Key Rules
1. **Check before importing** — `list_images` first
2. **Reuse directory\_state\_id** — Valid for entire session
3. **Use UUIDs directly** — Only tag frequently-used images
4. **One step per command** — Easier rollback and debugging
5. **Always exclude** — `__pycache__`, `.git`, `.venv`, `node_modules`
## Error Recovery
| Error | Solution |
| ------------------------- | ---------------------------------- |
| Image not found | `list_images` to find correct UUID |
| Directory state not found | Re-run `rsync` |
| Command timed out | Increase `timeout` parameter |
| Output truncated | Increase `truncate_output_at` |
| Operation stuck | `cancel_operation` + retry |
## Resources (Read-Only)
| URI Pattern | Returns |
| ------------------------------------ | ------------------------ |
| `contree://image/{uuid}/read/{path}` | File contents |
| `contree://image/{uuid}/ls/{path}` | Directory listing |
| `contree://image/{uuid}/lineage` | Parent-child history |
| `contree://guide/{section}` | Documentation |
| `contree://operations/instance/{id}` | Command execution result |
| `contree://operations/import/{id}` | Image import result |
# Core Concepts
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/concepts/core
How Contree runs code and manages container images.
## Execution Model
When you call `run`, Contree:
1. Spins up an isolated microVM (\~2-5 seconds)
2. Mounts the specified image as the filesystem
3. Injects any files from `directory_state_id` or `files`
4. Executes your command as root
5. Captures stdout, stderr, exit code
6. Optionally saves the resulting filesystem as a new image
**Isolation guarantees:** Every command runs in a separate kernel with full network/filesystem isolation. Destructive commands (`rm -rf /`, kernel exploits) are completely safe.
## The disposable Flag
| Setting | Behavior | Use Case |
| ---------------- | ----------------- | ----------------------------- |
| `true` (default) | Changes discarded | Tests, read-only operations |
| `false` | New image created | Installing packages, building |
**filesystem\_changed response field:**
* When `true`, `result_image` is a new UUID (changes were saved)
* When `false`, `result_image` equals input image (no snapshot created)
## Images
Every image is:
* **Immutable**: Once created, it never changes
* **Identified by UUID**: `abc123-def456-789012`
* **Optionally tagged**: Human-readable names like `python:3.11`
| Aspect | UUID | Tag |
| ----------- | ---------------------------- | ----------------------------------- |
| Immutable | Yes | Points to different UUIDs over time |
| When to use | Chaining, one-off operations | Frequently reused base images |
## Lineage
When you run with `disposable=false` and filesystem changes, Contree creates a parent-child relationship:
```
docker://alpine:latest (img-root)
└── apk add python3 (img-with-python)
├── pip install numpy (img-with-numpy)
└── pip install pandas (img-with-pandas)
```
**View lineage:**
```
contree://image/{uuid}/lineage
```
**Rollback:** Just use any ancestor UUID - no special command needed.
## Timeouts and Output
* **Default timeout**: 30 seconds (use `timeout` parameter for longer)
* **Default output limit**: 8000 bytes (\~2000 tokens)
* **Adjust with**: `truncate_output_at` parameter
# Concepts
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/concepts/index
Core ideas behind Contree MCP.
## Overview
| Concept | Description |
| ------------- | ------------------------------------------- |
| **Core** | Execution model, images, lineage, isolation |
| **Workflows** | File sync (rsync), async execution |
## Quick Mental Model
```mermaid theme={null}
flowchart LR
A[import_image] --> B[Base Image]
B --> C[run
disposable=false]
C --> D[Child Image]
D --> E[Another run]
B --> F[Different branch]
```
Every image is immutable. `disposable=false` creates a new child image. Navigate and rollback using any ancestor UUID.
# Workflows
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/concepts/workflows
File sync and async execution patterns.
## File Sync
Two ways to inject files into containers:
| Method | Use Case | Caching |
| -------- | ------------------------------- | ------------ |
| `rsync` | Directories, multiple files | Yes (3-tier) |
| `upload` | Single files, generated content | No |
### rsync (Preferred)
```json theme={null}
{"tool": "rsync", "args": {
"source": "/project",
"destination": "/app",
"exclude": ["__pycache__", ".git", ".venv", "node_modules"]
}}
```
Returns `directory_state_id` for use in `run`.
**Three-tier caching**: Local cache → Cache by hash → Server dedup → Upload. Most files resolve from cache.
**Reuse across runs**: The `directory_state_id` is valid for the session. Only re-sync when files change.
### upload (Single Files)
```json theme={null}
{"tool": "upload", "args": {"content": "print('hello')"}}
```
Reference in `run` via `files` parameter:
```json theme={null}
{"files": {"/app/script.py": "file-uuid"}}
```
## Async Execution
| Mode | Parameter | Behavior |
| ----- | --------------------- | ---------------------------------- |
| Sync | `wait=true` (default) | Blocks until complete |
| Async | `wait=false` | Returns `operation_id` immediately |
### Parallel Pattern
```json theme={null}
// Launch async
{"command": "python exp_a.py", "wait": false} // Returns op-1
{"command": "python exp_b.py", "wait": false} // Returns op-2
{"command": "python exp_c.py", "wait": false} // Returns op-3
// Wait for all
{"tool": "wait_operations", "args": {"operation_ids": ["op-1", "op-2", "op-3"]}}
```
### wait\_operations Modes
* `"all"` - Wait for all operations to complete
* `"any"` - Return when first operation completes
### Operation States
| State | Description |
| ----------- | ---------------------- |
| `PENDING` | Queued, not started |
| `EXECUTING` | Running |
| `SUCCESS` | Completed successfully |
| `FAILED` | Completed with error |
| `CANCELLED` | Cancelled by user |
### When to Use Async
**Use `wait=false`** when:
* Running 2+ independent operations
* Operations are long-running (>10s)
**Use `wait=true`** when:
* Running a single operation
* Operations must be sequential
# Overview
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/index
[](https://pypi.org/project/contree-mcp/)
[](https://github.com/nebius/contree-mcp/actions/workflows/tests.yml)
[](https://github.com/nebius/contree-mcp/blob/master/LICENSE)
**Isolated cloud container execution for AI agents.**
Contree MCP is a Model Context Protocol server that gives AI agents secure sandboxed environments with full root access, network, and persistent images. Experiment fearlessly—every container is isolated, every image is immutable, mistakes are free.
Run your first container in 5 minutes.
Understand execution model, images, file sync.
All 15 tools with parameters and examples.
Common workflows and mistakes to avoid.
10 MCP prompts for common workflows.
## Why Contree?
* **Safe sandbox**: Run `rm -rf /`, kernel exploits—nothing escapes
* **Immutable images**: Every UUID is a snapshot, branching is cheap
* **Instant rollback**: Revert to any previous image at zero cost
## Quick Example
```json theme={null}
{"tool": "list_images", "args": {"tag_prefix": "python"}}
{"tool": "rsync", "args": {"source": "/project", "destination": "/app"}}
{"tool": "run", "args": {
"command": "python /app/main.py",
"image": "img-uuid",
"directory_state_id": "ds-uuid"
}}
```
## HTTP Mode
Run the MCP server with built-in interactive documentation:
```bash theme={null}
contree-mcp --mode http --http-port 9452
```
Visit `http://localhost:9452/` for setup guides, tool reference, and best practices.
## Security
To report security issues, see [Security](./security).
## License
Licensed under the Apache License, Version 2.0. See LICENSE for details.
# Configuration
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/integration/configuration
Detailed configuration options for Contree MCP.
## Authentication
The MCP server supports both auth modes the Contree API exposes — the
same two `contree-cli` recognises:
* **IAM (recommended).** Token + Project header. Sends
`Authorization: Bearer ` and `Project: `.
Default URL: `https://api.tokenfactory.nebius.com/sandboxes`. Use this
for new deployments; the standard `NEBIUS_API_KEY` /
`NEBIUS_AI_PROJECT` env vars are honoured.
* **JWT (legacy).** Token only. Sends `Authorization: Bearer `.
Used by the `contree.dev` PoC deployment. No project required.
A profile’s `type = iam | jwt` line picks the scheme; the client class
that issues backend requests is wired accordingly at startup.
### Profile File (Recommended)
`contree-mcp` reads the same profile file that
[`contree-cli`](https://docs.contree.dev/cli/tutorial/installation.html)
writes, so a single login covers both tools. Install the CLI and run
`contree auth`:
```bash theme={null}
uv tool install contree-cli # or: pip install contree-cli
contree auth # interactive setup
```
This writes `~/.config/contree/auth.ini` with mode `0600`. Override
the directory with `CONTREE_HOME`.
The file is INI-format with one `[profile:]` section per
credential set and a `[DEFAULT] profile = ` line selecting the
active profile:
```ini theme={null}
[DEFAULT]
profile = default
[profile:default]
type = iam
url = https://api.tokenfactory.nebius.com/sandboxes
token =
project =
[profile:staging]
type = jwt
url = https://contree.dev
token =
```
Switch profiles persistently with `contree auth switch `, or
per-invocation with `--profile ` / `CONTREE_PROFILE=`.
`type = iam` requires `project`; `type = jwt` does not.
### Environment Variables (Per-Invocation Overrides)
For one-off overrides without touching the profile file:
```bash theme={null}
export CONTREE_TOKEN="your-token-here"
export CONTREE_URL="https://api.tokenfactory.nebius.com/sandboxes"
export CONTREE_PROJECT="your-nebius-project-id"
```
The standard Nebius IAM credentials are recognised too:
```bash theme={null}
export NEBIUS_API_KEY="your-iam-key"
export NEBIUS_AI_PROJECT="your-nebius-project-id"
```
#### Precedence (important)
Field-by-field, highest first:
1. **CLI flags** — `--token`, `--project`, `--url`, `--profile`.
2. **`CONTREE_*` env vars** — `CONTREE_TOKEN` / `CONTREE_PROJECT` /
`CONTREE_URL` / `CONTREE_PROFILE`. MCP-specific; always layered
on top of the profile.
3. **`NEBIUS_*` env vars** — `NEBIUS_API_KEY` + `NEBIUS_AI_PROJECT`,
recognised **only when both are set** (a complete IAM credential).
A lone `NEBIUS_API_KEY` set ambiently for the Nebius SDK or
terraform provider is **ignored**, and the MCP server logs an
`info` line explaining why.
4. **Active profile** from `auth.ini`. Picked by, in order, `--profile`
→ `CONTREE_PROFILE` → the file’s `[DEFAULT] profile = ...`.
Some practical implications:
* `contree-mcp` (no args) loads the active profile, even if your shell
has `NEBIUS_API_KEY` set for other tools.
* `CONTREE_TOKEN=NEW contree-mcp` rotates the token but reuses the
profile’s `project` and `url` — handy for short-lived tokens.
* `contree-mcp --token X --project Y` populates token + project from
the CLI; `url` and `auth_type` still come from the loaded profile
unless `--url` / `--auth-type` are also supplied.
* With no profile loaded and an incomplete `CONTREE_TOKEN` / `--token`,
the server stops with “No API token configured” rather than running
with half-set credentials.
Token resolution order, per field:
```
token --token > CONTREE_TOKEN > NEBIUS_API_KEY* > profile.token
project --project > CONTREE_PROJECT > NEBIUS_AI_PROJECT* > profile.project
url --url > CONTREE_URL > profile.url
> IAM default (IAM only)
```
`*` `NEBIUS_*` are read only when both are set.
Tokens passed via env may appear in process listings — prefer the
profile file for routine use.
## Server Options
| Option | Environment Variable | Default | Description |
| ------------------ | ------------------------- | ---------------------------------------------------------- | ------------------------------------------------------ |
| - | `CONTREE_HOME` | `$XDG_CONFIG_HOME/contree` (typically `~/.config/contree`) | Directory containing `auth.ini` and MCP state |
| `--profile` | `CONTREE_PROFILE` | active profile from `auth.ini` | Profile to use |
| `--token` | `CONTREE_TOKEN` | from profile | API token (overrides profile) |
| `--url` | `CONTREE_URL` | from profile | API base URL (overrides profile) |
| `--project` | `CONTREE_PROJECT` | from profile | Project ID for IAM auth |
| `--mode` | - | `stdio` | `stdio` or `http` |
| `--http-port` | - | `9452` | HTTP mode port |
| `--http-listen` | - | `127.0.0.1` | HTTP mode bind address |
| `--log-level` | - | `warning` | Logging level |
| `--version` / `-V` | - | - | Print the User-Agent the server emits and exit |
| - | `CONTREE_NO_UPDATE_CHECK` | unset | Disable the daily PyPI update check (set to any value) |
## Cache Configuration
| Option | Default |
| -------------------- | ------------------------------------------------------------------------- |
| `--cache-files` | `$CONTREE_HOME/mcp/files.db` (typically `~/.config/contree/mcp/files.db`) |
| `--cache-general` | `$CONTREE_HOME/mcp/cache.db` (typically `~/.config/contree/mcp/cache.db`) |
| `--cache-prune-days` | `60` |
## Client Configuration Examples
With credentials stored in `~/.config/contree/auth.ini`, MCP client
configs are minimal:
### Claude Code
```bash theme={null}
claude mcp add --transport stdio contree -- $(which uvx) contree-mcp
```
### HTTP Mode
For network access from other machines:
```bash theme={null}
contree-mcp --mode http --http-port 9452 --http-listen 0.0.0.0
```
Visit `http://localhost:9452/` for interactive documentation with setup guides, tool reference, and best practices.
## Manual Installation
```bash theme={null}
# Using uv
uv pip install contree-mcp
# Using pip
pip install contree-mcp
# Container environments (PEP 668)
pip install --break-system-packages contree-mcp
```
# Integration
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/integration/index
Setting up and configuring Contree MCP.
## Quick Setup
See the [Quickstart](../quickstart) for basic setup instructions.
## Configuration Options
Credentials live in a profile-based `auth.ini` shared with
[`contree-cli`](https://docs.contree.dev/cli/tutorial/installation.html);
install the CLI and run `contree auth` once to populate it. The MCP
server then reads the active profile automatically.
| Option | Environment Variable | Default |
| ------------------ | -------------------- | ---------------------------------------------------------- |
| - | `CONTREE_HOME` | `$XDG_CONFIG_HOME/contree` (typically `~/.config/contree`) |
| `--profile` | `CONTREE_PROFILE` | active profile from `auth.ini` |
| `--token` | `CONTREE_TOKEN` | from profile |
| `--url` | `CONTREE_URL` | from profile |
| `--project` | `CONTREE_PROJECT` | from profile (IAM auth only) |
| `--mode` | - | `stdio` |
| `--http-port` | - | `9452` |
| `--log-level` | - | `warning` |
| `--version` / `-V` | - | print User-Agent and exit |
Resolution priority for credentials: \*\*CLI flag > environment variable
> stored profile\*\*.
## Supported Clients
* Claude Code
* Claude Desktop
* OpenAI Codex CLI
* Any MCP-compatible client
## See Also
* [Configuration](./configuration) - Detailed config options
* [Troubleshooting](./troubleshooting) - Common issues
# Troubleshooting
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/integration/troubleshooting
Common issues and solutions.
## Connection Issues
### “No API token configured” Error
**Cause**: No active profile in `auth.ini` and no `--token` /
`CONTREE_TOKEN` override.
**Solution (recommended):** install `contree-cli` and run `contree auth`:
```bash theme={null}
uv tool install contree-cli # or: pip install contree-cli
contree auth # interactive setup
```
This writes `~/.config/contree/auth.ini`. The MCP server reads it
automatically.
For a one-off override:
```bash theme={null}
export CONTREE_TOKEN="your-token"
export CONTREE_URL="https://api.tokenfactory.nebius.com/sandboxes"
export CONTREE_PROJECT="your-nebius-project-id" # only for IAM auth
```
### Server logs `Ignoring NEBIUS_API_KEY: NEBIUS_AI_PROJECT is not set`
**Cause**: Your shell has `NEBIUS_API_KEY` set (typically for the
Nebius SDK or terraform provider) but no `NEBIUS_AI_PROJECT`. The MCP
server treats this as an incomplete IAM credential and falls back to
the active profile from `auth.ini`. This is *not* an error — it’s a
deliberate guard against an ambient env var hijacking your saved
profile.
**Solution**: nothing to do if you wanted the profile to load. If you
*meant* to authenticate via env, also export `NEBIUS_AI_PROJECT` (or
use the MCP-specific `CONTREE_TOKEN` / `CONTREE_PROJECT`, which layer
per-field on top of the profile and don’t have this guard).
### “IAM auth requires a project ID” Error
**Cause**: The active profile (or the env/CLI overrides) is IAM-typed
but no project ID was supplied.
**Solution**: either add `project = ...` under the matching
`[profile:]` section, set `CONTREE_PROJECT`, or pass
`--project `. For legacy JWT deployments, set `type = jwt` in the
profile — projects aren’t needed there.
### “Connection refused” Error
**Cause**: Server not reachable or wrong URL.
**Solution**: Verify the URL in the active profile (or pass
`--url`/`CONTREE_URL`):
```bash theme={null}
export CONTREE_URL="https://api.tokenfactory.nebius.com/sandboxes"
```
### “Forbidden” / Missing Permissions
**Cause**: The active token doesn’t have the permission your call
needs (`spawn`, `import`, `set_image_tag`, `cancel`, …).
**Solution**: ask the `whoami` tool to enumerate what’s available:
```json theme={null}
{"tool": "whoami"}
```
If a permission is `false`, the only recourse is a token with
different grants — the MCP server cannot escalate.
## Tool Errors
### “Image not found”
**Cause**: Invalid image UUID or tag.
**Solutions**:
1. Check with `list_images`
2. Ensure the UUID is correct
3. For tags, use `tag:` prefix: `"image": "tag:python:3.11"`
### “Directory state not found”
**Cause**: Invalid `directory_state_id` or expired session.
**Solution**: Call `rsync` again to get a new `directory_state_id`.
### “Operation timed out”
**Cause**: Command exceeded timeout.
**Solution**: Increase the timeout:
```json theme={null}
{"tool": "run", "args": {
"command": "...",
"timeout": 600
}}
```
## Performance Issues
### Slow File Sync
**Cause**: Large files or too many files.
**Solutions**:
1. Use exclusions:
```json theme={null}
{"exclude": ["node_modules", ".git", "__pycache__", "*.log"]}
```
2. Sync only what you need
3. Use glob patterns for specific files
### Commands Taking Long to Start
**Cause**: VM startup time (\~2-5 seconds).
**Solutions**:
1. Batch operations when possible
2. Use async for parallel operations
3. Reuse images with dependencies pre-installed
## Debugging
### Enable Debug Logging
Pass `--log-level debug` to the server:
```bash theme={null}
contree-mcp --log-level debug ...
```
In an MCP client config that spawns the server, append the flag to
`args`:
```json theme={null}
{
"mcpServers": {
"contree": {
"command": "uvx",
"args": ["contree-mcp", "--log-level", "debug"]
}
}
}
```
### Confirm the Installed Version
`contree-mcp --version` (or `-V`) prints the User-Agent the server
emits on every backend call. Useful when triaging which build is
actually wired into your client:
```bash theme={null}
$ contree-mcp --version
contree-mcp/0.1.1 Python/3.13.0.final.0 Linux-6.5.0-arm64
```
### Check Operation Status
For async operations:
```json theme={null}
{"tool": "get_operation", "args": {"operation_id": "op-..."}}
```
### View Image Lineage
To understand how an image was created:
```
contree://image/your-image-uuid/lineage
```
## Getting Help
* [GitHub Issues](https://github.com/nebius/contree/issues)
* Check the [Concepts](../concepts/index) for understanding
* See [Patterns](../patterns) for best practices
# Patterns
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/patterns
Common workflows and mistakes to avoid.
## Run Python Script with Local Files
```json theme={null}
// 1. Check for image
{"tool": "list_images", "args": {"tag_prefix": "python"}}
// 2. Import if needed
{"tool": "import_image", "args": {"registry_url": "docker://python:3.11-slim"}}
// 3. Sync files
{"tool": "rsync", "args": {
"source": "/project", "destination": "/app",
"exclude": ["__pycache__", ".git", ".venv"]
}}
// 4. Run
{"tool": "run", "args": {
"command": "python /app/main.py",
"image": "img-uuid",
"directory_state_id": "ds-xxx"
}}
```
## Install Dependencies and Save
```json theme={null}
// Save changes with disposable=false
{"tool": "run", "args": {
"command": "pip install numpy pandas",
"image": "tag:python:3.11",
"disposable": false
}}
// Returns: {"result_image": "img-with-deps"}
// Use new image for subsequent runs
{"tool": "run", "args": {
"command": "python /app/train.py",
"image": "img-with-deps",
"directory_state_id": "ds-xxx"
}}
```
**Mistake**: Forgetting `disposable=false` means changes are discarded.
## Parallel Execution
```json theme={null}
// Launch async
{"tool": "run", "args": {"command": "python exp_a.py", "image": "img", "wait": false}}
{"tool": "run", "args": {"command": "python exp_b.py", "image": "img", "wait": false}}
// Wait for all
{"tool": "wait_operations", "args": {"operation_ids": ["op-1", "op-2"]}}
```
**Mistake**: Using `wait=false` for single operations adds unnecessary complexity.
## Build and Extract Artifact
```json theme={null}
// Build with disposable=false
{"tool": "run", "args": {
"command": "cargo build --release",
"image": "tag:rust:1.75",
"directory_state_id": "ds-project",
"disposable": false,
"timeout": 300
}}
// Download result
{"tool": "download", "args": {
"image": "img-built",
"path": "/app/target/release/myapp",
"destination": "./myapp",
"executable": true
}}
```
## Rollback After Failure
```
// View lineage
contree://image/broken-uuid/lineage
// Returns: {"parent": {"image": "working-parent-uuid"}}
// Continue from working state
{"tool": "run", "args": {
"command": "python fixed.py",
"image": "working-parent-uuid"
}}
```
***
## Common Mistakes
### Re-syncing unchanged files
**Wrong**: Calling `rsync` before every `run`
**Right**: Sync once, reuse `directory_state_id` for all runs. Re-sync only when files change.
### Importing without checking
**Wrong**: `import_image` immediately
**Right**: `list_images` first to check if image exists
### Chaining commands in one string
**Wrong**:
```json theme={null}
{"command": "apt update && apt install python && pip install numpy && python train.py"}
```
**Right**: Run each step separately with `disposable=false`. Enables rollback if later steps fail.
### Using tags for one-off images
**Wrong**: Creating tags for temporary experiments
**Right**: Use UUIDs directly. Tags are for frequently-reused images.
### Not excluding build artifacts
**Wrong**: rsync without exclusions
**Right**: Always exclude `__pycache__`, `.git`, `.venv`, `node_modules`, `target`, `dist`
### Ignoring filesystem\_changed
When `filesystem_changed: false`, `result_image` equals input image - no new snapshot was created.
# build-project
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/build-project
Build a project: install dependencies and run tests.
## Description
The `build-project` prompt provides instructions for a standard build workflow: sync files, install dependencies, and run tests.
## Parameters
| Parameter | Type | Required | Default | Description |
| ------------- | ------ | -------- | ------------------ | ------------------------------- |
| `source` | string | Yes | - | Local directory path to sync |
| `install_cmd` | string | No | `pip install -e .` | Dependency installation command |
| `test_cmd` | string | No | `pytest` | Test execution command |
## Generated Instructions
When invoked with:
```json theme={null}
{
"source": "/home/user/myproject",
"install_cmd": "pip install -e '.[dev]'",
"test_cmd": "pytest -v tests/"
}
```
Returns:
```markdown theme={null}
Build and test the project:
1. Sync `/home/user/myproject` to `/app` using `rsync`
2. Import `python:3.11-slim` if needed
3. Install dependencies with `pip install -e '.[dev]'` (use `disposable=false`)
4. Run tests with `pytest -v tests/` on the result image
5. Report test results
```
## Example Usage
### Standard Python Project
```json theme={null}
{
"prompt": "build-project",
"args": {
"source": "/path/to/project"
}
}
```
### With Custom Commands
```json theme={null}
{
"prompt": "build-project",
"args": {
"source": "/path/to/project",
"install_cmd": "pip install -r requirements.txt",
"test_cmd": "python -m pytest --cov=src tests/"
}
}
```
### Poetry Project
```json theme={null}
{
"prompt": "build-project",
"args": {
"source": "/path/to/project",
"install_cmd": "pip install poetry && poetry install",
"test_cmd": "poetry run pytest"
}
}
```
## Implementation Notes
The agent should:
1. Sync files with `rsync`:
```json theme={null}
{
"source": "",
"destination": "/app",
"exclude": ["__pycache__", ".git", ".venv", "node_modules"]
}
```
2. Check for Python image, import if needed
3. Install dependencies with `run`:
```json theme={null}
{
"command": "",
"image": "tag:python:3.11-slim",
"directory_state_id": "",
"cwd": "/app",
"disposable": false
}
```
4. Run tests with `run`:
```json theme={null}
{
"command": "",
"image": "",
"directory_state_id": "",
"cwd": "/app"
}
```
5. Report test results (exit code, stdout, stderr)
## See Also
* [multi-stage-build](./multi-stage-build) - Complex builds with checkpoints
* [sync-and-run](./sync-and-run) - Simple file sync and execution
* [install-packages](./install-packages) - Just install packages
# debug-failure
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/debug-failure
Diagnose a failed command and suggest fixes.
## Description
The `debug-failure` prompt provides a systematic approach to diagnosing and fixing failed container operations.
## Parameters
| Parameter | Type | Required | Default | Description |
| -------------- | ------ | -------- | ------- | --------------------- |
| `operation_id` | string | Yes | - | Operation ID to debug |
## Generated Instructions
When invoked with:
```json theme={null}
{
"operation_id": "op-abc-123-def"
}
```
Returns:
```markdown theme={null}
Debug the failed operation `op-abc-123-def`:
1. Use `get_operation` to retrieve the operation result
2. Analyze:
- `exit_code`: Non-zero indicates command failure
- `stderr`: Contains error messages and stack traces
- `stdout`: May contain partial output or clues
- `timed_out`: If true, command exceeded timeout
3. Common issues to check:
- Missing files (file not found errors)
- Missing dependencies (import errors, command not found)
- Permission issues (though commands run as root)
- Timeout exceeded (increase timeout or optimize command)
4. Suggest specific fixes based on the error
5. If needed, re-run with fixes applied
```
## Example Usage
### Debug Build Failure
```json theme={null}
{
"prompt": "debug-failure",
"args": {
"operation_id": "op-build-failed-123"
}
}
```
### Debug Test Failure
```json theme={null}
{
"prompt": "debug-failure",
"args": {
"operation_id": "op-test-run-456"
}
}
```
## Common Error Patterns
### Missing Dependencies
**Symptom:** `ModuleNotFoundError` or `command not found`
**Solution:** Install missing packages with `install-packages` or use a prepared environment.
### File Not Found
**Symptom:** `No such file or directory`
**Solution:** Verify path with `list_files`, ensure files are synced with `rsync`.
### Timeout
**Symptom:** `timed_out: true`
**Solution:** Increase `timeout` parameter or break into smaller operations.
### Permission Denied
**Symptom:** `Permission denied`
**Solution:** Commands run as root, so check if file exists and is accessible.
## Implementation Notes
The agent should:
1. Retrieve operation with `get_operation`:
```json theme={null}
{"operation_id": ""}
```
2. Analyze the result:
* Check `exit_code` (0 = success, non-zero = failure)
* Read `stderr` for error messages
* Check `timed_out` flag
* Review `stdout` for partial output
3. Diagnose based on error patterns:
* Import errors → missing packages
* File not found → path issues
* Timeout → need more time or optimization
4. Suggest and implement fixes
## See Also
* [inspect-image](./inspect-image) - Explore image contents
* [Error Handling Guide](../resources) - Common errors and solutions
* [build-project](./build-project) - Retry build after fix
# Prompts Reference
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/index
MCP prompts for common Contree workflows. Prompts provide structured instructions that guide AI agents through multi-step tasks.
## Quick Reference
| Prompt | Description | Key Parameters |
| -------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------ |
| [prepare-environment](./prepare-environment) | Prepare container environment with CHECK-PREPARE-EXECUTE flow | `task`, `base`, `project`, `packages` |
| [run-python](./run-python) | Run Python code in isolated container | `code` |
| [run-shell](./run-shell) | Run shell command in isolated container | `command`, `image` |
| [sync-and-run](./sync-and-run) | Sync local files and run command | `source`, `command`, `image` |
| [install-packages](./install-packages) | Install packages and create reusable image | `packages`, `image` |
| [parallel-tasks](./parallel-tasks) | Run multiple tasks in parallel | `tasks`, `image` |
| [build-project](./build-project) | Build project: install deps and run tests | `source`, `install_cmd`, `test_cmd` |
| [debug-failure](./debug-failure) | Diagnose failed operation | `operation_id` |
| [inspect-image](./inspect-image) | Explore container image contents | `image` |
| [multi-stage-build](./multi-stage-build) | Multi-stage build with rollback points | `source`, `install_cmd`, `build_cmd`, `test_cmd` |
## Using Prompts
### With MCP Clients
MCP-compatible clients can invoke prompts directly:
```json theme={null}
{
"prompt": "prepare-environment",
"args": {
"task": "Train ML model",
"base": "python:3.11-slim",
"packages": "numpy pandas scikit-learn"
}
}
```
### Prompt Output
Prompts return structured instructions that guide the agent through:
1. **Step-by-step workflows** - Ordered operations with clear dependencies
2. **Tool selection** - Which Contree tools to use and when
3. **Parameter guidance** - Correct values for each tool call
4. **Best practices** - Following the CHECK-PREPARE-EXECUTE pattern
## Categories
### Environment Setup
* [prepare-environment](./prepare-environment) - Full workflow with environment reuse
* [install-packages](./install-packages) - Install and tag for reuse
### Code Execution
* [run-python](./run-python) - Quick Python execution
* [run-shell](./run-shell) - Shell command execution
* [sync-and-run](./sync-and-run) - Local files + execution
### Building and Testing
* [build-project](./build-project) - Standard build + test workflow
* [multi-stage-build](./multi-stage-build) - Complex builds with checkpoints
### Operations
* [parallel-tasks](./parallel-tasks) - Concurrent execution
* [debug-failure](./debug-failure) - Error diagnosis
* [inspect-image](./inspect-image) - Image exploration
# inspect-image
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/inspect-image
Explore the contents of a container image.
## Description
The `inspect-image` prompt provides instructions for thoroughly examining a container image to understand its contents, installed software, and configuration.
## Parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ------- | ---------------------------- |
| `image` | string | Yes | - | Image UUID or tag to inspect |
## Generated Instructions
When invoked with:
```json theme={null}
{
"image": "tag:python:3.11-slim"
}
```
Returns:
```markdown theme={null}
Inspect the container image `tag:python:3.11-slim`:
1. Use `run` with the image to explore:
- `ls -la /` - List root directory contents
- `cat /etc/os-release` - Check OS version
- `which python pip node` - Find installed tools
- `pip list` or `dpkg -l` - List installed packages
2. Report findings:
- Operating system and version
- Available languages/runtimes
- Key installed packages
- Notable files or directories
3. Use `disposable=true` (default) since we're just exploring
```
## Example Usage
### Explore Python Image
```json theme={null}
{
"prompt": "inspect-image",
"args": {
"image": "tag:python:3.11-slim"
}
}
```
### Explore Custom Image
```json theme={null}
{
"prompt": "inspect-image",
"args": {
"image": "abc123-def456-uuid"
}
}
```
### Explore Alpine
```json theme={null}
{
"prompt": "inspect-image",
"args": {
"image": "tag:alpine:latest"
}
}
```
## Implementation Notes
The agent should use the free inspection tools first, then `run` for dynamic queries:
1. **Use `list_files` first** (free, no VM):
```json theme={null}
{"image": "", "path": "/"}
{"image": "", "path": "/etc"}
{"image": "", "path": "/usr/local/bin"}
```
2. **Use `read_file` for config files** (free, no VM):
```json theme={null}
{"image": "", "path": "/etc/os-release"}
```
3. **Use `run` for dynamic queries** (spawns VM):
```json theme={null}
{"command": "pip list", "image": ""}
{"command": "which python pip node", "image": ""}
```
4. Report findings in a structured format:
* OS: Debian 12 / Alpine 3.18 / etc.
* Languages: Python 3.11, Node.js 20, etc.
* Key packages: numpy, flask, etc.
* Notable directories: /app, /data, etc.
## See Also
* [list\_files](../tools/list_files) - List directory contents (free)
* [read\_file](../tools/read_file) - Read file contents (free)
* [debug-failure](./debug-failure) - Debug after inspection
# install-packages
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/install-packages
Install packages and create a reusable image.
## Description
The `install-packages` prompt provides instructions for installing packages in a container and tagging the result for future reuse. It follows the prepare-and-tag pattern.
## Parameters
| Parameter | Type | Required | Default | Description |
| ---------- | ------ | -------- | ------------------ | ------------------------------------- |
| `packages` | string | Yes | - | Packages to install (space-separated) |
| `image` | string | No | `python:3.11-slim` | Base image to use |
## Generated Instructions
When invoked with:
```json theme={null}
{
"packages": "flask gunicorn",
"image": "python:3.11-slim"
}
```
Returns:
```markdown theme={null}
Install packages in a container:
1. Check if base image `tag:python:3.11-slim` exists with `list_images`
2. If not, import it with `import_image`
3. Run `pip install flask gunicorn` with `disposable=false` to save the image
4. Tag the result for reuse (e.g., `claude/common/python/custom-deps:3.11`)
The returned `result_image` can be used for subsequent commands.
```
## Example Usage
### Python Packages
```json theme={null}
{
"prompt": "install-packages",
"args": {
"packages": "numpy pandas matplotlib scikit-learn"
}
}
```
### Web Framework Stack
```json theme={null}
{
"prompt": "install-packages",
"args": {
"packages": "fastapi uvicorn sqlalchemy alembic"
}
}
```
### System Packages
For system packages, use a different base image and package manager:
```json theme={null}
{
"prompt": "install-packages",
"args": {
"packages": "curl wget git",
"image": "ubuntu:22.04"
}
}
```
Note: For Ubuntu, the agent should adapt to use `apt install` instead of `pip install`.
## Implementation Notes
The agent should:
1. Check if base image exists with `list_images`
2. Import if needed with `import_image`
3. Install packages with `run`:
```json theme={null}
{
"command": "pip install ",
"image": "tag:",
"disposable": false
}
```
4. Tag the result with `set_tag`:
```json theme={null}
{
"image_uuid": "",
"tag": "common//"
}
```
## See Also
* [prepare-environment](./prepare-environment) - Full environment preparation
* [build-project](./build-project) - Build with dependencies
* [multi-stage-build](./multi-stage-build) - Complex multi-stage builds
# multi-stage-build
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/multi-stage-build
Multi-stage build with rollback points.
## Description
The `multi-stage-build` prompt provides instructions for complex builds with multiple stages, each creating a checkpoint for potential rollback.
## Parameters
| Parameter | Type | Required | Default | Description |
| ------------- | ------ | -------- | ------------------ | ------------------------------- |
| `source` | string | Yes | - | Local directory path to sync |
| `install_cmd` | string | No | `pip install -e .` | Dependency installation command |
| `build_cmd` | string | No | `python -m build` | Build command |
| `test_cmd` | string | No | `pytest` | Test command |
## Generated Instructions
When invoked with:
```json theme={null}
{
"source": "/home/user/project",
"install_cmd": "pip install -e '.[dev]'",
"build_cmd": "python -m build",
"test_cmd": "pytest -v"
}
```
Returns:
```markdown theme={null}
Execute a multi-stage build with rollback checkpoints:
Source: `/home/user/project`
**Stage 1: Setup Base**
1. Check if `tag:python:3.11-slim` exists, import if needed
2. Sync source files with `rsync`
**Stage 2: Install Dependencies** (checkpoint: `deps-installed`)
1. Run `pip install -e '.[dev]'` with `disposable=false`
2. Save `result_image` as rollback point
3. If this fails, report error and stop
**Stage 3: Build** (checkpoint: `build-complete`)
1. Run `python -m build` on deps image with `disposable=false`
2. Save `result_image` as rollback point
3. If this fails, can rollback to deps-installed image
**Stage 4: Test**
1. Run `pytest -v` on build image (disposable=true for tests)
2. Report test results
3. If tests fail, can rollback to build-complete or deps-installed
**Rollback Strategy:**
- Keep track of each stage's `result_image` UUID
- On failure, report which checkpoint to resume from
- Previous checkpoints remain valid for retry
```
## Example Usage
### Python Package Build
```json theme={null}
{
"prompt": "multi-stage-build",
"args": {
"source": "/path/to/package"
}
}
```
### Custom Build Pipeline
```json theme={null}
{
"prompt": "multi-stage-build",
"args": {
"source": "/path/to/project",
"install_cmd": "pip install poetry && poetry install",
"build_cmd": "poetry build",
"test_cmd": "poetry run pytest --cov"
}
}
```
### Rust Project
```json theme={null}
{
"prompt": "multi-stage-build",
"args": {
"source": "/path/to/rust-project",
"install_cmd": "cargo fetch",
"build_cmd": "cargo build --release",
"test_cmd": "cargo test"
}
}
```
## Stage Details
### Stage 1: Setup Base
* Ensures base image exists
* Syncs source files
* No checkpoint needed (base image is the checkpoint)
### Stage 2: Install Dependencies
* Runs install command with `disposable=false`
* Saves result as `deps-installed` checkpoint
* **Rollback target:** If install fails, fix and retry from base
### Stage 3: Build
* Runs build command with `disposable=false`
* Saves result as `build-complete` checkpoint
* **Rollback target:** If build fails, can retry from `deps-installed`
### Stage 4: Test
* Runs tests with `disposable=true` (no need to save test artifacts)
* Reports results
* **Rollback targets:** Can retry from `build-complete` or `deps-installed`
## Implementation Notes
The agent should track checkpoints:
```
base_image = "tag:python:3.11-slim"
|
v
deps_image = run(install_cmd, disposable=false).result_image
|
v
build_image = run(build_cmd, image=deps_image, disposable=false).result_image
|
v
test_result = run(test_cmd, image=build_image, disposable=true)
```
On failure at any stage, report:
* Which stage failed
* Available rollback points
* How to resume
## See Also
* [build-project](./build-project) - Simpler build workflow
* [debug-failure](./debug-failure) - Debug build failures
* [State Management Guide](../resources) - Rollback patterns
# parallel-tasks
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/parallel-tasks
Run multiple tasks in parallel.
## Description
The `parallel-tasks` prompt provides instructions for executing multiple independent tasks concurrently using Contree’s async execution capabilities.
## Parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ------------------ | --------------------------- |
| `tasks` | string | Yes | - | Tasks to run (one per line) |
| `image` | string | No | `python:3.11-slim` | Base image to use |
## Generated Instructions
When invoked with:
```json theme={null}
{
"tasks": "python test_unit.py\npython test_integration.py\npython test_e2e.py",
"image": "python:3.11-slim"
}
```
Returns:
```markdown theme={null}
Run these tasks in parallel:
Tasks (one per line):
python test_unit.py
python test_integration.py
python test_e2e.py
Steps:
1. Ensure image `tag:python:3.11-slim` exists (import if needed)
2. Call `run` for each task with `wait=false` - make parallel tool calls
3. Collect all `operation_id` values
4. Use `wait_operations` to wait for all to complete
5. Report results from each task
```
## Example Usage
### Parallel Tests
```json theme={null}
{
"prompt": "parallel-tasks",
"args": {
"tasks": "pytest tests/unit/\npytest tests/integration/\npytest tests/e2e/"
}
}
```
### Multiple Experiments
```json theme={null}
{
"prompt": "parallel-tasks",
"args": {
"tasks": "python train.py --lr 0.001\npython train.py --lr 0.01\npython train.py --lr 0.1"
}
}
```
### Build Multiple Targets
```json theme={null}
{
"prompt": "parallel-tasks",
"args": {
"tasks": "cargo build --target x86_64-unknown-linux-gnu\ncargo build --target aarch64-unknown-linux-gnu",
"image": "rust:1.75"
}
}
```
## Implementation Notes
The agent should:
1. Ensure the image exists (check with `list_images`, import if needed)
2. Launch all tasks in parallel with `wait=false`:
```json theme={null}
// Make these calls in parallel
{"command": "python test_unit.py", "image": "tag:python:3.11-slim", "wait": false}
{"command": "python test_integration.py", "image": "tag:python:3.11-slim", "wait": false}
{"command": "python test_e2e.py", "image": "tag:python:3.11-slim", "wait": false}
```
3. Collect the returned `operation_id` values
4. Wait for all with `wait_operations`:
```json theme={null}
{"operation_ids": ["op-1", "op-2", "op-3"]}
```
5. Process and report results from each operation
## See Also
* [build-project](./build-project) - Sequential build workflow
* [debug-failure](./debug-failure) - Debug failed operations
* [Async Guide](../resources) - Async execution patterns
# prepare-environment
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/prepare-environment
Prepare a container environment for a task, checking for existing images first.
## Description
The `prepare-environment` prompt implements the recommended CHECK-PREPARE-EXECUTE workflow:
1. **CHECK** - Search for existing prepared environments
2. **PREPARE** - Import and configure if not found
3. **EXECUTE** - Run the task with the prepared environment
This ensures maximum reuse of prepared images and follows Contree best practices.
## Parameters
| Parameter | Type | Required | Default | Description |
| ---------- | ------ | -------- | ------------------ | --------------------------------------------------- |
| `task` | string | Yes | - | Description of the task to perform |
| `base` | string | No | `python:3.11-slim` | Base image to use |
| `project` | string | No | `None` | Project name for scoping (uses `common` if not set) |
| `packages` | string | No | `None` | Packages to install (space-separated) |
## Generated Instructions
When invoked with:
```json theme={null}
{
"task": "Train ML model",
"base": "python:3.11-slim",
"packages": "numpy pandas scikit-learn"
}
```
Returns instructions like:
````markdown theme={null}
Prepare an environment for: Train ML model
**Step 1: CHECK for Existing Environment**
Search for prepared environments:
```json
// list_images
{"tag_prefix": "common/"}
````
If a suitable environment exists (has required packages), skip to Step 3.
**Step 2: PREPARE Environment (if not found)**
2a. Import base image:
```json theme={null}
// import_image
{"registry_url": "docker://docker.io/python:3.11-slim"}
```
2b. Install dependencies with `disposable=false`:
```json theme={null}
// run
{
"command": "pip install numpy pandas scikit-learn",
"image": "",
"disposable": false
}
```
2c. Tag for reuse:
```json theme={null}
// set_tag
{"image_uuid": "", "tag": "common/numpy-env/python:3.11-slim"}
```
**Step 3: EXECUTE Task**
Use the prepared environment:
```json theme={null}
// run
{"command": "", "image": "tag:common/numpy-env/python:3.11-slim"}
```
```
## Tag Generation
The prompt automatically generates a tag following the convention:
```
\{scope}/\{purpose}/\{base}:\{tag}
````
- **scope**: Uses `project` parameter if provided, otherwise `common`
- **purpose**: Derived from first package name (e.g., `numpy-env`) or `custom-env`
- **base:tag**: From `base` parameter
## Example Usage
### ML Development Environment
```json
{
"prompt": "prepare-environment",
"args": {
"task": "Run data analysis notebook",
"packages": "jupyter pandas matplotlib seaborn"
}
}
````
### Project-Specific Environment
```json theme={null}
{
"prompt": "prepare-environment",
"args": {
"task": "Run API tests",
"project": "myproject",
"packages": "pytest requests httpx"
}
}
```
Generates tag: `myproject/pytest-env/python:3.11-slim`
### Custom Base Image
```json theme={null}
{
"prompt": "prepare-environment",
"args": {
"task": "Compile Rust project",
"base": "rust:1.75-slim"
}
}
```
## See Also
* [install-packages](./install-packages) - Simpler package installation
* [build-project](./build-project) - Full build workflow
* [Tagging Convention](../resources) - Tag naming guide
# run-python
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/run-python
Run Python code in an isolated container.
## Description
The `run-python` prompt provides a simple way to execute Python code in a container. It handles image selection and provides clear instructions for the execution.
## Parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ------- | ---------------------- |
| `code` | string | Yes | - | Python code to execute |
## Generated Instructions
When invoked with:
```json theme={null}
{
"code": "import sys\nprint(f'Python {sys.version}')"
}
```
Returns:
````markdown theme={null}
Run this Python code in a container:
```python
import sys
print(f'Python {sys.version}')
````
Use `run` with `tag:python:3.11-slim` image. If the image doesn’t exist, import it first.
````
## Example Usage
### Simple Calculation
```json
{
"prompt": "run-python",
"args": {
"code": "print(sum(range(100)))"
}
}
````
### Multi-line Script
```json theme={null}
{
"prompt": "run-python",
"args": {
"code": "def factorial(n):\n return 1 if n <= 1 else n * factorial(n-1)\n\nfor i in range(10):\n print(f'{i}! = {factorial(i)}')"
}
}
```
### With Package Usage
```json theme={null}
{
"prompt": "run-python",
"args": {
"code": "import numpy as np\nprint(np.random.rand(5))"
}
}
```
Note: If the code requires packages not in the base image, you’ll need to install them first using `install-packages` or `prepare-environment`.
## Implementation Notes
The agent should:
1. Check if `tag:python:3.11-slim` exists with `list_images`
2. If not found, import it with `import_image`
3. Execute the code with `run`:
```json theme={null}
{
"command": "python -c ''",
"image": "tag:python:3.11-slim"
}
```
## See Also
* [run-shell](./run-shell) - Run shell commands
* [sync-and-run](./sync-and-run) - Run with local files
* [install-packages](./install-packages) - Install dependencies first
# run-shell
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/run-shell
Run a shell command in an isolated container.
## Description
The `run-shell` prompt provides a simple way to execute shell commands in a container with a specified base image.
## Parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | -------------- | ------------------------ |
| `command` | string | Yes | - | Shell command to execute |
| `image` | string | No | `ubuntu:22.04` | Base image to use |
## Generated Instructions
When invoked with:
```json theme={null}
{
"command": "uname -a && cat /etc/os-release",
"image": "alpine:latest"
}
```
Returns:
````markdown theme={null}
Run this command in a container:
```bash
uname -a && cat /etc/os-release
````
Use image `tag:alpine:latest`. If the image doesn’t exist, import it first with `import_image`.
````
## Example Usage
### Basic Command
```json
{
"prompt": "run-shell",
"args": {
"command": "ls -la /etc"
}
}
````
### With Specific Image
```json theme={null}
{
"prompt": "run-shell",
"args": {
"command": "go version",
"image": "golang:1.21"
}
}
```
### System Information
```json theme={null}
{
"prompt": "run-shell",
"args": {
"command": "df -h && free -m && nproc"
}
}
```
## Implementation Notes
The agent should:
1. Check if the specified image exists with `list_images`
2. If not found, import it with `import_image`
3. Execute the command with `run`:
```json theme={null}
{
"command": "",
"image": "tag:"
}
```
## See Also
* [run-python](./run-python) - Run Python code
* [sync-and-run](./sync-and-run) - Run with local files
* [inspect-image](./inspect-image) - Explore image contents
# sync-and-run
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/prompts/sync-and-run
Sync local files to container and run a command.
## Description
The `sync-and-run` prompt provides instructions for syncing local project files to a container and executing a command. It handles the rsync workflow with proper exclusions.
## Parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ------------------ | ---------------------------- |
| `source` | string | Yes | - | Local directory path to sync |
| `command` | string | Yes | - | Command to run after syncing |
| `image` | string | No | `python:3.11-slim` | Base image to use |
## Generated Instructions
When invoked with:
```json theme={null}
{
"source": "/home/user/myproject",
"command": "python main.py",
"image": "python:3.11-slim"
}
```
Returns:
```markdown theme={null}
Sync files and run command:
1. Use `rsync` to sync `/home/user/myproject` to `/app` in the container
- Exclude: `__pycache__`, `.git`, `node_modules`, `.venv`
2. Use `run` with the returned `directory_state_id`
- Image: `tag:python:3.11-slim` (import if needed)
- Command: `python main.py`
- Working directory: `/app`
```
## Example Usage
### Python Project
```json theme={null}
{
"prompt": "sync-and-run",
"args": {
"source": "/path/to/project",
"command": "pytest tests/"
}
}
```
### Node.js Project
```json theme={null}
{
"prompt": "sync-and-run",
"args": {
"source": "/path/to/webapp",
"command": "npm test",
"image": "node:20-slim"
}
}
```
### Build and Run
```json theme={null}
{
"prompt": "sync-and-run",
"args": {
"source": "/path/to/rust-project",
"command": "cargo build --release && ./target/release/myapp",
"image": "rust:1.75"
}
}
```
## Implementation Notes
The agent should:
1. Use `rsync` to sync files:
```json theme={null}
{
"source": "",
"destination": "/app",
"exclude": ["__pycache__", ".git", "node_modules", ".venv"]
}
```
2. Check if the image exists, import if needed
3. Execute with `run`:
```json theme={null}
{
"command": "",
"image": "tag:",
"directory_state_id": "",
"cwd": "/app"
}
```
## See Also
* [build-project](./build-project) - Full build workflow
* [run-python](./run-python) - Simple Python execution
* [run-shell](./run-shell) - Simple shell execution
# Quickstart
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/quickstart
Run your first container in 5 minutes.
## Prerequisites
* An MCP-compatible client (Claude Code, Claude Desktop, or OpenAI Codex CLI)
* A Contree API token
### Getting an API Token
Contree is in **Early Access**. To get an API token, fill out the request form at [contree.dev](https://contree.dev).
## Installation
### Step 1: Authenticate
`contree-mcp` reads the same `auth.ini` that
[`contree-cli`](https://docs.contree.dev/cli/tutorial/installation.html)
writes, so a single login covers both tools.
**Recommended:** install `contree-cli` and run `contree auth`:
```bash theme={null}
uv tool install contree-cli # or: pip install contree-cli
contree auth # interactive setup
```
This writes `~/.config/contree/auth.ini` (mode `0600`). The MCP server
picks it up automatically.
If you prefer to write the file by hand:
```ini theme={null}
[DEFAULT]
profile = default
[profile:default]
type = iam
url = https://api.tokenfactory.nebius.com/sandboxes
token =
project =
```
For the legacy JWT flow (`contree.dev`), use:
```ini theme={null}
[profile:default]
type = jwt
url = https://contree.dev
token =
```
To switch between profiles later: `contree auth switch `, or
pass `--profile ` / `CONTREE_PROFILE=` to `contree-mcp`.
### Step 2: Configure Your MCP Client
```bash theme={null}
claude mcp add --transport stdio contree -- $(which uvx) contree-mcp
```
Restart Claude Code or run `/mcp` to verify.
Add to config file:
* **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
```json theme={null}
{
"mcpServers": {
"contree": {
"command": "uvx",
"args": ["contree-mcp"]
}
}
}
```
Add to `~/.codex/config.toml`:
```toml theme={null}
[mcp_servers.contree]
command = "uvx"
args = ["contree-mcp"]
```
You can also pass credentials via environment variables (`CONTREE_TOKEN`,
`CONTREE_URL`, `CONTREE_PROJECT`, `CONTREE_PROFILE`) or CLI flags
(`--token`, `--url`, `--project`, `--profile`). These are useful for
ephemeral overrides — but tokens passed via env may show up in process
listings, so for routine use prefer the `auth.ini` profile written by
`contree auth`.
## Your First Container
### Step 1: Check Available Images
```json theme={null}
{"tool": "list_images", "args": {"tag_prefix": "python", "limit": 5}}
```
If you don’t have any Python images, import one:
```json theme={null}
{"tool": "import_image", "args": {"registry_url": "docker://python:3.11-slim"}}
```
Response:
```json theme={null}
{
"result_image": "abc123-def456-...",
"state": "SUCCESS"
}
```
### Step 2: Run a Command
```json theme={null}
{
"tool": "run",
"args": {
"command": "python -c \"print('Hello from Contree!')\"",
"image": "abc123-def456-..."
}
}
```
Response:
```json theme={null}
{
"exit_code": 0,
"stdout": "Hello from Contree!\n",
"state": "SUCCESS"
}
```
### Step 3: Run with Local Files
First, sync your files:
```json theme={null}
{
"tool": "rsync",
"args": {
"source": "/path/to/your/project",
"destination": "/app",
"exclude": ["__pycache__", ".git", ".venv"]
}
}
```
Response:
```json theme={null}
{
"directory_state_id": "ds_xyz789...",
"stats": {"uploaded": 5, "cached": 10}
}
```
Then run with the synced files:
```json theme={null}
{
"tool": "run",
"args": {
"command": "python /app/main.py",
"image": "abc123-def456-...",
"directory_state_id": "ds_xyz789..."
}
}
```
## What’s Next?
Understand images, lineage, and async execution.
Common workflows and best practices.
Detailed parameters for all 15 tools.
MCP resources and guide sections.
# Glossary
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/reference/glossary
Terms and definitions used in Contree MCP.
## A
* **Async Execution** — Running operations without waiting for completion. Use `wait=false` and poll with `get_operation` or `wait_operations`.
## D
* **Directory State** — A snapshot of local files synced with `rsync`. Identified by `directory_state_id`.
* **Disposable** — When `disposable=true` (default), filesystem changes are discarded after command execution.
## I
* **Image** — An immutable filesystem snapshot. Identified by UUID.
* **Image Lineage** — The parent-child relationships between images. View with `contree://image/{uuid}/lineage`.
## M
* **MCP (Model Context Protocol)** — The protocol used for AI agent tool communication.
* **MicroVM** — A lightweight virtual machine used for isolated command execution.
## O
* **Operation** — A running or completed task (command execution or image import). Identified by `operation_id`.
## R
* **Result Image** — The image UUID returned when running with `disposable=false` and filesystem changes occur.
* **Root Image** — An image imported from a container registry, with no parent.
## T
* **Tag** — A human-readable name for an image (e.g., `python:3.11`). Tags can point to different UUIDs over time.
## U
* **UUID** — Universally Unique Identifier. The primary way to reference images and operations.
# Reference
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/reference/index
Additional reference materials.
## Quick Links
* [Glossary](./glossary) - Terms and definitions
# Resources
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/resources
MCP resource templates for reading files and metadata. No VM needed.
## image\_file
Read a file from a container image.
**URI**: `contree://image/{image}/read/{path}`
| Parameter | Description |
| --------- | ------------------------------------ |
| `image` | Image UUID or `tag:name` |
| `path` | Path inside image (no leading slash) |
**Examples:**
```
contree://image/abc123-uuid/read/etc/passwd
contree://image/tag:alpine:latest/read/etc/os-release
contree://image/tag:python:3.11/read/usr/local/lib/python3.11/site-packages/pip/__init__.py
```
**Returns:** Text content or base64-encoded binary.
***
## image\_ls
List directory contents in a container image.
**URI**: `contree://image/{image}/ls/{path}`
| Parameter | Description |
| --------- | ----------------------------- |
| `image` | Image UUID or `tag:name` |
| `path` | Directory path (`.` for root) |
**Examples:**
```
contree://image/abc123-uuid/ls/.
contree://image/tag:python:3.11/ls/usr/local/lib
```
**Returns:** JSON with file listing (path, size, mode, is\_dir, mtime).
***
## image\_lineage
View image parent-child relationships and history.
**URI**: `contree://image/{image}/lineage`
| Parameter | Description |
| --------- | ----------- |
| `image` | Image UUID |
**Example:**
```
contree://image/abc123-uuid/lineage
```
**Returns:**
```json theme={null}
{
"image": "abc123",
"parent": {"image": "parent-uuid", "command": "pip install numpy"},
"children": [],
"ancestors": [],
"root": {"image": "root-uuid", "registry_url": "docker://alpine:latest"},
"depth": 2
}
```
**Use for:** Rollback (use any ancestor UUID), understanding history.
***
## guide
Agent guides and best practices.
**URI**: `contree://guide/{section}`
| Section | Description |
| ------------ | --------------------------------------------- |
| `workflow` | Complete workflow patterns with decision tree |
| `reference` | Tool reference and quick lookup |
| `quickstart` | Common workflows and best practices |
| `state` | Image state, rollback, disposable mode |
| `async` | Parallel execution patterns |
| `tagging` | Agent tagging conventions |
| `errors` | Error handling and debugging |
**Examples:**
```
contree://guide/workflow
contree://guide/quickstart
contree://guide/async
contree://guide/errors
```
***
## instance\_operation
Read instance (command execution) operation details from cache.
**URI**: `contree://operations/instance/{operation_id}`
| Parameter | Description |
| -------------- | ------------------------------------------- |
| `operation_id` | Operation UUID from `run` with `wait=false` |
**Example:**
```
contree://operations/instance/op-abc-123-def
```
**Returns:**
```json theme={null}
{
"state": "SUCCESS",
"exit_code": 0,
"stdout": "Hello, World!",
"stderr": "",
"result_image": "uuid-of-result",
"resources": {"cpu_time_ms": 150, "memory_mb": 64}
}
```
**Use for:** Retrieving cached results of completed command executions.
***
## import\_operation
Read image import operation details from cache.
**URI**: `contree://operations/import/{operation_id}`
| Parameter | Description |
| -------------- | ---------------------------------------------------- |
| `operation_id` | Operation UUID from `import_image` with `wait=false` |
**Example:**
```
contree://operations/import/op-xyz-789-abc
```
**Returns:**
```json theme={null}
{
"state": "SUCCESS",
"registry_url": "docker://python:3.11-slim",
"result_image": "uuid-of-imported-image",
"result_tag": "python:3.11-slim"
}
```
**Use for:** Retrieving cached results of completed image imports.
# Reporting Security Issues
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/security
The Nebius team take security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and
will make every effort to acknowledge your contributions.
To report a security issue, please use the GitHub Security Advisory
[“Report a Vulnerability”](https://github.com/nebius/contree-mcp/security/advisories/new) tab.
The Nebius team will send a response indicating the next steps in handling your report. After the initial reply to your
report, the Nebius team will keep you informed of the progress towards a fix and full announcement, and may ask for
additional information or guidance.
## Learning More About Security in Nebius
To learn more about security in Nebius, please see [this page](https://nebius.ai/docs/security).
# cancel_operation
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/cancel_operation
Cancel a running operation.
## TL;DR
* **Use when**: Operation taking too long, no longer needed
* **Returns**: Cancellation status
* **Cost**: No VM needed
## Parameters
| Parameter | Type | Required | Default | Description |
| -------------- | ------ | -------- | ------- | ------------------------ |
| `operation_id` | string | Yes | - | Operation UUID to cancel |
## Response
```json theme={null}
{
"success": true,
"operation_id": "op-abc123"
}
```
## Examples
### Cancel Operation
```json theme={null}
{"tool": "cancel_operation", "args": {
"operation_id": "op-abc123"
}}
```
## See Also
* [get\_operation](./get_operation) - Check operation status
* [list\_operations](./list_operations) - Find operations
# download
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/download
Download a file from a container image to local filesystem.
## TL;DR
* **Use when**: Extracting build artifacts, logs, binaries
* **Returns**: Success status, file size, path
* **Cost**: No VM needed
## Parameters
| Parameter | Type | Required | Default | Description |
| ------------- | ------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `image` | string | Yes | - | Image UUID or `tag:name` |
| `path` | string | Yes | - | Path inside container |
| `destination` | string | Yes | - | Absolute path on MCP host filesystem (`~` supported, parent dirs auto-created). This writes to the MCP server’s filesystem, not inside the container. |
| `executable` | boolean | No | `false` | Make file executable |
## Response
```json theme={null}
{
"success": true,
"size": 12345,
"size_human": "12.1 KB",
"destination": "/local/path/binary",
"source": {
"image": "img-build-result",
"path": "/app/dist/binary"
},
"executable": true
}
```
## Examples
### Download Build Artifact
```json theme={null}
{"tool": "download", "args": {
"image": "img-build-result",
"path": "/app/dist/binary",
"destination": "~/downloads/binary",
"executable": true
}}
```
### Download Log File
```json theme={null}
{"tool": "download", "args": {
"image": "img-uuid",
"path": "/var/log/app.log",
"destination": "~/downloads/debug.log"
}}
```
> **Note:** `destination` must be an absolute path on the MCP server’s host filesystem (not inside the container). Use `~` for home directory. Parent directories are created automatically.
## See Also
* [upload](./upload) - Upload files to Contree
* [rsync](./rsync) - Sync directories
# get_guide
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/get_guide
Get agent guide sections for Contree best practices.
## Overview
`get_guide` provides access to documentation and best practices for using Contree. This tool is an alternative to the `contree://guide/{section}` resource for agents that don’t support MCP resources.
## Parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ------- | ------------------ |
| `section` | string | Yes | - | Guide section name |
### Available Sections
| Section | Description |
| ------------ | --------------------------------------------- |
| `workflow` | Complete workflow patterns with decision tree |
| `reference` | Tool reference with parameters and data flow |
| `quickstart` | Quick examples for common operations |
| `state` | State management and rollback patterns |
| `async` | Parallel execution patterns |
| `tagging` | Agent tagging conventions |
| `errors` | Error handling and debugging |
## Returns
| Field | Type | Description |
| -------------------- | ------ | ----------------------------------- |
| `section` | string | Requested section name |
| `content` | string | Guide content in Markdown |
| `available_sections` | array | List of all available section names |
## Cost
**Free** - No VM spawned. Returns static documentation.
## Examples
### Get Workflow Guide
```json theme={null}
{
"tool": "get_guide",
"args": {
"section": "workflow"
}
}
```
Response:
```json theme={null}
{
"section": "workflow",
"content": "# Contree Workflow Guide\n\n## Decision Tree: Which Image to Use?\n...",
"available_sections": ["async", "errors", "quickstart", "reference", "state", "tagging", "workflow"]
}
```
### Get Error Handling Guide
```json theme={null}
{
"tool": "get_guide",
"args": {
"section": "errors"
}
}
```
### Get Tagging Convention
```json theme={null}
{
"tool": "get_guide",
"args": {
"section": "tagging"
}
}
```
## When to Use
Use `get_guide` when:
* Your agent runtime doesn’t support MCP resources
* You need documentation about Contree best practices
* You want to understand workflow patterns or error handling
If your agent supports MCP resources, prefer using the resource URI:
```
contree://guide/workflow
contree://guide/errors
```
## Guide Content Overview
### workflow
Decision trees for choosing images, complete examples for Python ML environments, anti-patterns to avoid, and project-specific environment guidance.
### reference
Quick reference table of all tools with parameters, returns, and costs. Detailed parameter documentation for key tools.
### quickstart
Basic command execution, file sync patterns, dependency chains, and best practices for UUIDs vs tags.
### state
Understanding immutable snapshots, disposable mode, rollback model with branching, and response fields.
### async
Sequential vs parallel patterns, launching multiple async operations, waiting for results, and operation states.
### tagging
Tag format convention `{scope}/{purpose}/{base}:{tag}`, when to tag as common vs project-specific, and common tags to search for.
### errors
Common error patterns (command failures, timeouts, missing images), solutions, and debugging workflow.
## See Also
* [Resources](../resources) - `contree://guide/{section}` resource alternative
* [list\_images](./list_images) - Find existing tagged images
* [set\_tag](./set_tag) - Tag images for reuse
# get_image
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/get_image
Get image details by UUID or tag.
## TL;DR
* **Use when**: Verifying an image exists, resolving tag to UUID
* **Returns**: Image UUID, tag, creation time
* **Cost**: No VM needed
## Parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ------- | ------------------------ |
| `image` | string | Yes | - | Image UUID or `tag:name` |
## Response
```json theme={null}
{
"uuid": "abc123-def456-...",
"tag": "python:3.11",
"created_at": "2024-01-15T10:30:00Z"
}
```
## Examples
### By UUID
```json theme={null}
{"tool": "get_image", "args": {"image": "abc123-def456-..."}}
```
### By Tag
```json theme={null}
{"tool": "get_image", "args": {"image": "tag:python:3.11"}}
```
## See Also
* [list\_images](./list_images) - List all images
* [set\_tag](./set_tag) - Assign a tag
# get_operation
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/get_operation
Get the status and result of an operation.
## TL;DR
* **Use when**: Checking async operation results
* **Returns**: Operation state, stdout/stderr, exit code
* **Cost**: No VM needed
## Parameters
| Parameter | Type | Required | Default | Description |
| -------------- | ------ | -------- | ------- | -------------- |
| `operation_id` | string | Yes | - | Operation UUID |
## Response
For instance (command) operations:
```json theme={null}
{
"operation_kind": "instance",
"state": "SUCCESS",
"exit_code": 0,
"stdout": "output here",
"stderr": null,
"result_image": "img-uuid",
"resources": {
"elapsed_time": 1.234
}
}
```
For import operations:
```json theme={null}
{
"operation_kind": "image_import",
"state": "SUCCESS",
"result_image": "img-uuid",
"result_tag": "python:3.11"
}
```
## Examples
### Check Status
```json theme={null}
{"tool": "get_operation", "args": {
"operation_id": "op-abc123"
}}
```
## Operation States
| State | Description |
| ----------- | ---------------------- |
| `PENDING` | Queued |
| `EXECUTING` | Running |
| `SUCCESS` | Completed successfully |
| `FAILED` | Completed with error |
| `CANCELLED` | Cancelled |
## See Also
* [wait\_operations](./wait_operations) - Wait for multiple ops
* [list\_operations](./list_operations) - List all operations
* [cancel\_operation](./cancel_operation) - Cancel running op
# import_image
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/import_image
Import OCI container image from registry. Spawns microVM.
**Check first**: Use `list_images` to see if already imported.
## Authentication
Before importing, authenticate with the registry:
1. Call `registry_token_obtain` to open browser for PAT creation
2. User creates read-only PAT in registry web UI
3. Call `registry_auth` to validate and store credentials
Anonymous access is possible but discouraged due to registry provider rate limits.
## Parameters
| Parameter | Type | Default | Description |
| ------------------------------------------------------ | ------- | -------- | ------------------------------------------- |
| `registry_url` | string | required | Registry URL (e.g., `docker://python:3.11`) |
| `tag` | string | - | Tag to assign after import |
| `wait` | boolean | `true` | Wait for completion |
| `i_accept_that_anonymous_access_might_be_rate_limited` | boolean | `false` | Skip authentication (not recommended) |
## Examples
**Basic (requires prior authentication):**
```json theme={null}
{"registry_url": "docker://python:3.11-slim"}
```
**With tag:**
```json theme={null}
{"registry_url": "docker://alpine:latest", "tag": "alpine:latest"}
```
**Anonymous access (rate limited):**
```json theme={null}
{"registry_url": "docker://alpine:latest", "i_accept_that_anonymous_access_might_be_rate_limited": true}
```
**Async:**
```json theme={null}
{"registry_url": "docker://pytorch/pytorch:2.0-cuda11.7", "wait": false}
```
## Response
```json theme={null}
{"result_image": "abc123-uuid", "result_tag": "python:3.11", "state": "SUCCESS"}
```
With `wait=false`: `{"operation_id": "op-xxx"}`
## Common Base Images
| Registry URL | Use Case |
| --------------------------- | ------------- |
| `docker://python:3.11-slim` | Python |
| `docker://node:20-slim` | Node.js |
| `docker://alpine:latest` | Minimal Linux |
| `docker://ubuntu:22.04` | Full Linux |
| `docker://golang:1.21` | Go |
## Parallel Imports
```json theme={null}
{"registry_url": "docker://python:3.11", "wait": false}
{"registry_url": "docker://node:20", "wait": false}
{"tool": "wait_operations", "args": {"operation_ids": ["op-1", "op-2"]}}
```
# Tools Reference
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/index
All 17 tools for container execution, file management, and operations.
## Quick Reference
### Command Execution
| Tool | Description | Cost |
| ------------ | ---------------------------- | -------------- |
| [run](./run) | Execute command in container | Spawns microVM |
### File Transfer
| Tool | Description | Cost |
| ---------------------- | ----------------------------- | ----- |
| [rsync](./rsync) | Sync local files with caching | No VM |
| [upload](./upload) | Upload single file | No VM |
| [download](./download) | Download file from image | No VM |
### Image Management
| Tool | Description | Cost |
| -------------------------------------------------- | ------------------------------ | -------------- |
| [import\_image](./import_image) | Import from registry | Spawns microVM |
| [registry\_token\_obtain](./registry_token_obtain) | Open browser for PAT creation | No VM |
| [registry\_auth](./registry_auth) | Validate and store credentials | No VM |
| [list\_images](./list_images) | List available images | No VM |
| [get\_image](./get_image) | Get image by UUID/tag | No VM |
| [set\_tag](./set_tag) | Set or remove tag | No VM |
### Image Inspection
| Tool | Description | Cost |
| --------------------------- | -------------------- | ----- |
| [list\_files](./list_files) | List files in image | No VM |
| [read\_file](./read_file) | Read file from image | No VM |
### Operations
| Tool | Description | Cost |
| --------------------------------------- | --------------------- | ----- |
| [get\_operation](./get_operation) | Get operation status | No VM |
| [list\_operations](./list_operations) | List operations | No VM |
| [wait\_operations](./wait_operations) | Wait for multiple ops | No VM |
| [cancel\_operation](./cancel_operation) | Cancel operation | No VM |
### Documentation
| Tool | Description | Cost |
| ------------------------- | ------------------ | ----- |
| [get\_guide](./get_guide) | Get guide sections | No VM |
## Common Patterns
### Basic Execution
```json theme={null}
{"tool": "run", "args": {"command": "python -c 'print(1)'", "image": "img-uuid"}}
```
### With Local Files
```json theme={null}
{"tool": "rsync", "args": {"source": "/project", "destination": "/app"}}
{"tool": "run", "args": {"command": "python /app/main.py", "image": "img-uuid", "directory_state_id": "ds-uuid"}}
```
### Parallel Execution
```json theme={null}
{"tool": "run", "args": {"command": "test1.py", "image": "img", "wait": false}}
{"tool": "run", "args": {"command": "test2.py", "image": "img", "wait": false}}
{"tool": "wait_operations", "args": {"operation_ids": ["op-1", "op-2"]}}
```
### Inspect Container (No VM)
```json theme={null}
{"tool": "list_files", "args": {"image": "img-uuid", "path": "/etc"}}
{"tool": "read_file", "args": {"image": "img-uuid", "path": "/etc/os-release"}}
```
# list_files
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/list_files
List files and directories in a container image without spawning a VM.
## Overview
`list_files` provides instant filesystem inspection without the overhead of starting a container. Use it to explore image contents, verify file existence, and check permissions before running commands.
## Parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ------- | ------------------------ |
| `image` | string | Yes | - | Image UUID or `tag:name` |
| `path` | string | No | `/` | Directory path to list |
## Returns
| Field | Type | Description |
| ------- | ------- | ------------------------------- |
| `path` | string | Normalized path that was listed |
| `count` | integer | Number of entries in listing |
| `files` | array | List of file entries |
### File Entry Fields
| Field | Type | Description |
| -------- | ------- | ------------------------------------- |
| `name` | string | File or directory name |
| `path` | string | Full path within image |
| `type` | string | `file`, `directory`, or `symlink` |
| `size` | integer | Size in bytes |
| `mode` | string | Octal permission mode (e.g., `0o755`) |
| `target` | string | Symlink target (only for symlinks) |
## Cost
**Free** - No VM spawned. Reads directly from image filesystem.
## Examples
### List Root Directory
```json theme={null}
{
"tool": "list_files",
"args": {
"image": "abc123-def456",
"path": "/"
}
}
```
Response:
```json theme={null}
{
"path": "/",
"count": 15,
"files": [
{"name": "bin", "path": "/bin", "type": "symlink", "size": 0, "mode": "0o777", "target": "usr/bin"},
{"name": "etc", "path": "/etc", "type": "directory", "size": 4096, "mode": "0o755", "target": null},
{"name": "root", "path": "/root", "type": "directory", "size": 4096, "mode": "0o700", "target": null}
]
}
```
### List Specific Directory
```json theme={null}
{
"tool": "list_files",
"args": {
"image": "tag:python:3.11-slim",
"path": "/usr/local/lib/python3.11"
}
}
```
### Verify File Existence Before Running
```json theme={null}
// Check if expected file exists
{"tool": "list_files", "args": {"image": "img-uuid", "path": "/app"}}
// If found, run the command
{"tool": "run", "args": {"command": "python /app/main.py", "image": "img-uuid"}}
```
## Best Practices
* **Prefer over `run("ls")`** - `list_files` is instant and free
* **Verify paths before commands** - Check files exist to avoid errors
* **Explore unfamiliar images** - Understand structure before running code
* **Check permissions** - Verify executable bits and ownership
## See Also
* [read\_file](./read_file) - Read file contents without VM
* [run](./run) - Execute commands (spawns VM)
* [Resources](../resources) - `contree://image/{image}/ls/{path}` resource alternative
# list_images
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/list_images
List available container images.
## TL;DR
* **Use when**: Finding images, checking before import
* **Returns**: List of images with UUID, tag, creation time
* **Cost**: No VM needed
## Parameters
| Parameter | Type | Required | Default | Description |
| ------------ | ------- | -------- | ------- | -------------------------------- |
| `limit` | integer | No | `100` | Max images to return (1-1000) |
| `offset` | integer | No | `0` | Skip first N images |
| `tagged` | boolean | No | `null` | Only tagged images |
| `tag_prefix` | string | No | `null` | Filter by tag prefix |
| `since` | string | No | `null` | Created after (e.g., “1h”, “1d”) |
| `until` | string | No | `null` | Created before |
## Response
```json theme={null}
{
"images": [
{
"uuid": "abc123-def456-...",
"tag": "python:3.11",
"created_at": "2024-01-15T10:30:00Z"
},
{
"uuid": "xyz789-...",
"tag": null,
"created_at": "2024-01-15T09:00:00Z"
}
]
}
```
## Examples
### List All
```json theme={null}
{"tool": "list_images", "args": {}}
```
### Filter by Tag Prefix
```json theme={null}
{"tool": "list_images", "args": {"tag_prefix": "python"}}
```
### Only Tagged Images
```json theme={null}
{"tool": "list_images", "args": {"tagged": true}}
```
### Recent Images
```json theme={null}
{"tool": "list_images", "args": {"since": "1h"}}
```
### Pagination
```json theme={null}
{"tool": "list_images", "args": {"limit": 10, "offset": 20}}
```
## See Also
* [import\_image](./import_image) - Import new images
* [get\_image](./get_image) - Get single image details
# list_operations
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/list_operations
List operations (running or completed).
## TL;DR
* **Use when**: Finding operation IDs, monitoring
* **Returns**: List of operations with status
* **Cost**: No VM needed
## Parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------- | -------- | ------- | ------------------------ |
| `limit` | integer | No | `100` | Max operations to return |
| `status` | string | No | `null` | Filter by status |
| `kind` | string | No | `null` | Filter by kind |
| `since` | string | No | `null` | Created after |
## Response
```json theme={null}
{
"operations": [
{
"uuid": "op-abc123",
"kind": "instance",
"state": "SUCCESS",
"created_at": "2024-01-15T10:30:00Z"
}
]
}
```
## Examples
### List Running
```json theme={null}
{"tool": "list_operations", "args": {"status": "running"}}
```
### List by Kind
```json theme={null}
{"tool": "list_operations", "args": {"kind": "image_import"}}
```
## See Also
* [get\_operation](./get_operation) - Get single operation
* [cancel\_operation](./cancel_operation) - Cancel operation
# read_file
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/read_file
Read a file from a container image without spawning a VM.
## Overview
`read_file` provides instant file content access without the overhead of starting a container. Use it to inspect configuration files, review scripts, or check expected content before running commands.
## Parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ------- | ------------------------ |
| `image` | string | Yes | - | Image UUID or `tag:name` |
| `path` | string | Yes | - | File path to read |
## Returns
| Field | Type | Description |
| ------------ | ------- | ----------------------------------------------- |
| `path` | string | Normalized path that was read |
| `content` | string | File contents (decoded or base64 encoded) |
| `bytes_size` | integer | Size in bytes |
| `encoding` | string | `"utf-8"` for text files, `"base64"` for binary |
## Cost
**Free** - No VM spawned. Reads directly from image filesystem.
## Text Detection
Files are automatically detected as text based on extension. Common text extensions include:
* Source code: `.py`, `.js`, `.ts`, `.go`, `.rs`, `.java`, `.c`, `.h`, `.cpp`
* Config: `.json`, `.yaml`, `.yml`, `.toml`, `.ini`, `.cfg`, `.conf`
* Scripts: `.sh`, `.bash`, `.zsh`
* Documentation: `.md`, `.rst`, `.txt`
* Web: `.html`, `.css`, `.xml`
Binary files are decoded with replacement characters for non-UTF8 bytes.
## Examples
### Read Configuration File
```json theme={null}
{
"tool": "read_file",
"args": {
"image": "abc123-def456",
"path": "/etc/os-release"
}
}
```
Response:
```json theme={null}
{
"path": "/etc/os-release",
"content": "PRETTY_NAME=\"Debian GNU/Linux 12 (bookworm)\"\nNAME=\"Debian GNU/Linux\"\nVERSION_ID=\"12\"\n...",
"bytes_size": 187,
"encoding": "utf-8"
}
```
### Check Python Package Version
```json theme={null}
{
"tool": "read_file",
"args": {
"image": "tag:python:3.11-slim",
"path": "/usr/local/lib/python3.11/site-packages/pip/__init__.py"
}
}
```
### Review Script Before Execution
```json theme={null}
// Read the script to understand what it does
{"tool": "read_file", "args": {"image": "img-uuid", "path": "/app/setup.sh"}}
// If safe, execute it
{"tool": "run", "args": {"command": "bash /app/setup.sh", "image": "img-uuid"}}
```
## Best Practices
* **Prefer over `run("cat")`** - `read_file` is instant and free
* **Review before executing** - Check scripts for safety before running
* **Inspect configurations** - Understand image setup without running commands
* **Verify expected content** - Check files contain what you expect
## See Also
* [list\_files](./list_files) - List directory contents without VM
* [download](./download) - Download file to local filesystem
* [Resources](../resources) - `contree://image/{image}/read/{path}` resource alternative
# registry_auth
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/registry_auth
Authenticate with a container registry via Personal Access Token.
## Parameters
| Parameter | Type | Default | Description |
| -------------- | ------ | -------- | ------------------------------------------------- |
| `registry_url` | string | required | Registry URL (e.g., `docker://ghcr.io/org/image`) |
| `username` | string | required | Registry username |
| `token` | string | required | Personal Access Token |
## URL Parsing
| Input | Registry |
| ------------------------------------- | -------------------- |
| `docker://ghcr.io/org/image` | ghcr.io |
| `oci://registry.gitlab.com/org/image` | registry.gitlab.com |
| `alpine` or `library/alpine` | docker.io (implicit) |
## Examples
**Docker Hub:**
```json theme={null}
{"registry_url": "docker://docker.io/library/alpine", "username": "myuser", "token": "dckr_pat_xxx"}
```
**GitHub Container Registry:**
```json theme={null}
{"registry_url": "docker://ghcr.io/org/image", "username": "myuser", "token": "ghp_xxx"}
```
## Response
**Success:**
```json theme={null}
{
"status": "success",
"registry": "docker.io",
"message": "Authenticated with 'docker.io' as 'myuser' successfully."
}
```
**Invalid credentials:**
```json theme={null}
{
"status": "error",
"registry": "docker.io",
"message": "Invalid credentials for 'docker.io'. Please verify your username and PAT."
}
```
## Token Storage
* Credentials are validated via OCI /v2/ API before storage
* Stored in local cache and persisted across sessions
* Tokens are revalidated before each `import_image` call
* Expired tokens are automatically removed from cache
## Workflow
1. Call `registry_token_obtain` → opens browser
2. User creates read-only PAT in registry web UI
3. User provides username and token
4. Call `registry_auth` → validates and stores credentials
5. Call `import_image` to import images
# registry_token_obtain
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/registry_token_obtain
Open browser to create a Personal Access Token for a container registry.
## Parameters
| Parameter | Type | Default | Description |
| -------------- | ------ | -------- | ------------------------------------------------- |
| `registry_url` | string | required | Registry URL (e.g., `docker://ghcr.io/org/image`) |
## Known Registries
| Registry | PAT Page |
| ------------------- | -------------------------- |
| docker.io | Docker Hub PAT settings |
| ghcr.io | GitHub fine-grained tokens |
| registry.gitlab.com | GitLab PAT settings |
| gcr.io | Google Cloud credentials |
## Examples
**Docker Hub:**
```json theme={null}
{"registry_url": "docker://docker.io/library/alpine"}
```
**GitHub Container Registry:**
```json theme={null}
{"registry_url": "docker://ghcr.io/org/image"}
```
**Bare image name (defaults to docker.io):**
```json theme={null}
{"registry_url": "alpine"}
```
## Response
**Success:**
```json theme={null}
{
"status": "success",
"registry": "docker.io",
"url": "https://app.docker.com/settings/personal-access-tokens",
"message": "Browser opened to ... Create a read-only PAT, then provide your username and token.",
"agent_instruction": "STOP HERE. Wait for user to create PAT and provide the token."
}
```
**Unknown registry:**
```json theme={null}
{
"status": "error",
"registry": "unknown.example.com",
"message": "Unknown registry 'unknown.example.com'. Please consult the registry documentation for token creation."
}
```
## Workflow
1. Call `registry_token_obtain` → opens browser
2. User creates read-only PAT in registry web UI
3. User provides username and token
4. Call `registry_auth` to validate and store credentials
5. Call `import_image` to import images
# rsync
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/rsync
Sync local files to Contree with smart caching. No VM needed.
## Parameters
| Parameter | Type | Default | Description |
| ------------- | ------ | -------- | -------------------------- |
| `source` | string | required | Local path or glob pattern |
| `destination` | string | required | Container target directory |
| `exclude` | array | `[]` | Patterns to exclude |
## Examples
**Basic:**
```json theme={null}
{"source": "/project", "destination": "/app"}
```
**With exclusions:**
```json theme={null}
{
"source": "/project",
"destination": "/app",
"exclude": ["__pycache__", "*.pyc", ".git", ".venv", "node_modules"]
}
```
**Glob pattern:**
```json theme={null}
{"source": "/project/**/*.py", "destination": "/app"}
```
## Response
Returns an integer `directory_state_id` for use with the `run` tool:
```json theme={null}
42
```
## Using with run
```json theme={null}
// 1. Sync
{"tool": "rsync", "args": {"source": "/project", "destination": "/app"}}
// Returns: 42
// 2. Run (reuse directory_state_id for multiple runs)
{"tool": "run", "args": {
"command": "python /app/main.py",
"image": "uuid",
"directory_state_id": 42
}}
```
## Caching
Three-tier: local cache → content hash → server dedup. Only changed files upload.
**Tip:** Reuse `directory_state_id` for the session. Re-sync only when files change.
## Recommended Exclusions
```json theme={null}
["__pycache__", "*.pyc", ".git", ".venv", "node_modules", "*.log", ".DS_Store"]
```
# run
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/run
Execute command in isolated container. Spawns microVM (\~2-5s startup).
## Parameters
| Parameter | Type | Default | Description |
| -------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------ |
| `command` | string | required | Shell command to execute |
| `image` | string | required | Image UUID or `tag:name` |
| `shell` | boolean | `true` | Whether command is a shell expression |
| `disposable` | boolean | `true` | Discard changes after execution |
| `directory_state_id` | integer | - | Files from rsync |
| `files` | object | - | Map of container paths to upload UUIDs: `{"/path/in/container": "uuid"}` — key is destination, value is UUID |
| `wait` | boolean | `true` | Block until complete |
| `timeout` | integer | `30` | Max seconds |
| `env` | object | - | Environment variables |
| `cwd` | string | `/root` | Working directory |
| `stdin` | string | - | Input via stdin |
| `truncate_output_at` | integer | `8000` | Max bytes for output |
## Examples
**Basic:**
```json theme={null}
{"command": "python --version", "image": "tag:python:3.11"}
```
**With local files:**
```json theme={null}
{"command": "python /app/main.py", "image": "uuid", "directory_state_id": 42}
```
**Save changes:**
```json theme={null}
{"command": "pip install flask", "image": "uuid", "disposable": false}
```
Returns: `{"result_image": "new-uuid", "filesystem_changed": true}`
**Async:**
```json theme={null}
{"command": "python long_task.py", "image": "uuid", "wait": false}
```
Returns: `{"operation_id": "op-xxx"}`
**With uploaded files:**
```json theme={null}
// Step 1: Upload file
{"tool": "upload", "args": {"content": "print('hello')"}}
// Returns: {"uuid": "file-uuid-123"}
// Step 2: Inject into container and run
{"tool": "run", "args": {
"command": "python /app/script.py",
"image": "tag:python:3.11",
"files": {"/app/script.py": "file-uuid-123"}
}}
```
> **Common mistake:** The `files` key is the **container path** (destination), the value is the **UUID** (from upload). Not the other way around. One UUID can be mounted to multiple paths.
**Environment variables:**
```json theme={null}
{"command": "echo $MY_VAR", "image": "uuid", "env": {"MY_VAR": "hello"}}
```
## Response
```json theme={null}
{
"exit_code": 0,
"timed_out": false,
"state": "SUCCESS",
"result_image": "uuid-if-disposable-false",
"filesystem_changed": true,
"stdout": "output",
"stderr": null
}
```
## Errors
* **Image not found**: Use `list_images` to find valid UUIDs
* **Directory state not found**: Re-run `rsync`
* **timed\_out: true**: Increase `timeout` parameter
# set_tag
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/set_tag
Set or remove a tag for an image.
## TL;DR
* **Use when**: Naming frequently-used images
* **Returns**: Updated image details
* **Cost**: No VM needed
## Parameters
| Parameter | Type | Required | Default | Description |
| ------------ | ------ | -------- | ------- | ------------------------------ |
| `image_uuid` | string | Yes | - | Image UUID to tag |
| `tag` | string | No | `null` | Tag to assign (omit to remove) |
## Response
```json theme={null}
{
"uuid": "abc123-def456-...",
"tag": "my-image:v1",
"created_at": "2024-01-15T10:30:00Z"
}
```
## Examples
### Set Tag
```json theme={null}
{"tool": "set_tag", "args": {
"image_uuid": "abc123-def456-...",
"tag": "claude/project/python/dev-env:v1"
}}
```
### Remove Tag
```json theme={null}
{"tool": "set_tag", "args": {
"image_uuid": "abc123-def456-..."
}}
```
## Tagging Convention
For AI agents, use this pattern:
```
{agent}/{project}/{base}/{approach}:{version}
```
Examples:
* `claude/myproject/python/dev-env:v1`
* `claude/common/alpine/build-tools:latest`
## See Also
* [get\_image](./get_image) - Get image details
* [list\_images](./list_images) - Find images
# upload
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/upload
Upload a file to Contree.
## TL;DR
* **Use when**: Single file, generated content
* **Returns**: File UUID for use with `run`
* **Cost**: No VM needed
* **Prefer**: `rsync` for multiple files (has caching)
## Parameters
| Parameter | Type | Required | Default | Description |
| ---------------- | ------ | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `content` | string | No\* | - | Text content |
| `content_base64` | string | No\* | - | Base64-encoded binary |
| `path` | string | No\* | - | Absolute path to a file on **local filesystem** to read and upload. Not a destination name — files are content-addressable (UUID). To name the file in a container, use `run`’s `files` parameter. |
\*One of `content`, `content_base64`, or `path` is required.
## Response
```json theme={null}
{
"uuid": "file-uuid-123",
"sha256": "abc123..."
}
```
## Examples
### Text Content
```json theme={null}
{"tool": "upload", "args": {
"content": "print('hello world')"
}}
```
### Local File
```json theme={null}
{"tool": "upload", "args": {
"path": "/path/to/script.py"
}}
```
### Binary (Base64)
```json theme={null}
{"tool": "upload", "args": {
"content_base64": "SGVsbG8gV29ybGQ="
}}
```
## Using the Result
Pass the UUID to `run` via the `files` parameter:
```json theme={null}
// Step 1: Upload
{"tool": "upload", "args": {"content": "print('hello')"}}
// Returns: {"uuid": "file-uuid-123"}
// Step 2: Run
{"tool": "run", "args": {
"command": "python /app/script.py",
"image": "img-uuid",
"files": {"/app/script.py": "file-uuid-123"}
}}
```
## Common Mistake
`path` reads from the **local MCP-server filesystem** — it does not set the file’s name in storage. The uploaded file is content-addressable and identified only by UUID. To place it at a specific path inside a container, pass the UUID to `run`’s `files` parameter:
```json theme={null}
{"tool": "run", "args": {
"command": "python /app/script.py",
"image": "img-uuid",
"files": {"/app/script.py": "file-uuid-123"}
}}
```
## See Also
* [rsync](./rsync) - For multiple files (with caching)
* [run](./run) - Use uploaded files
* [download](./download) - Download from images
# wait_operations
Source: https://docs.tokenfactory.nebius.com/sandboxes/mcp/tools/wait_operations
Wait for multiple operations to complete.
## TL;DR
* **Use when**: Launched multiple async operations
* **Returns**: All operation results
* **Cost**: No VM needed (just waiting)
## Parameters
| Parameter | Type | Required | Default | Description |
| --------------- | ------ | -------- | ------- | ----------------------- |
| `operation_ids` | array | Yes | - | List of operation UUIDs |
| `mode` | string | No | `"all"` | `"all"` or `"any"` |
| `timeout` | number | No | `300` | Max wait time (seconds) |
## Response
```json theme={null}
{
"results": {
"op-1": {
"state": "SUCCESS",
"exit_code": 0,
"stdout": "..."
},
"op-2": {
"state": "SUCCESS",
"exit_code": 0,
"stdout": "..."
}
},
"completed": ["op-1", "op-2"],
"cancelled": [],
"timed_out": false
}
```
## Examples
### Wait for All
```json theme={null}
{"tool": "wait_operations", "args": {
"operation_ids": ["op-1", "op-2", "op-3"]
}}
```
### Wait for Any (First to Complete)
```json theme={null}
{"tool": "wait_operations", "args": {
"operation_ids": ["op-1", "op-2", "op-3"],
"mode": "any"
}}
```
### With Timeout
```json theme={null}
{"tool": "wait_operations", "args": {
"operation_ids": ["op-1", "op-2"],
"timeout": 600
}}
```
## Parallel Execution Pattern
```json theme={null}
// Launch async
{"tool": "run", "args": {"command": "test1.py", "wait": false}}
{"tool": "run", "args": {"command": "test2.py", "wait": false}}
{"tool": "run", "args": {"command": "test3.py", "wait": false}}
// Wait for all
{"tool": "wait_operations", "args": {
"operation_ids": ["op-1", "op-2", "op-3"]
}}
```
## See Also
* [get\_operation](./get_operation) - Check single operation
* [Async Guide](../resources#guide) - Parallel execution patterns
# Overview
Source: https://docs.tokenfactory.nebius.com/sandboxes/overview
Sandboxes are currently in Beta. We value your feedback, please send us email to [contree@nebius.com](mailto:contree@nebius.com) or reach out in [Discord](https://discord.com/channels/1222156136380235877/1476967257585221845).
Sandboxes is a cloud-based sandbox API by Nebius that enables secure code execution with Git-like branching capabilities. It is built for AI agents that need to explore multiple execution paths, evaluate outcomes, and backtrack when necessary.
Sandboxes combines VM-level isolation with container efficiency, providing a secure environment for executing untrusted code. Unlike traditional containers, Sandboxes supports Git-like branching — fork from any checkpoint, run parallel explorations, and roll back instantly.
## Key features
VM-level isolation ensures untrusted code cannot escape the sandbox or affect other workloads.
Fork execution state at any checkpoint. Explore multiple solution paths in parallel, then score results and expand the best branches.
Return to any previous state with a single API call. No need to rebuild or re-execute from scratch.
Import images from any OCI-compliant registry (Docker Hub, GHCR, and others). Use your existing container images as sandbox bases.
Built-in tracking of CPU time, memory usage, and I/O operations for every execution.
All long-running operations (image imports, executions) are async with polling support and cancellation.
## Quick start
Start from a preloaded environment, a previously produced checkpoint, or an imported OCI image.
Attach the files and runtime assumptions the command needs so the run can be replayed later.
Run code inside the sandbox from the chosen state. Long-running work is represented as an operation.
Poll through the CLI, SDK, or generated client until execution reaches a terminal state.
Read logs, metrics, files, and artifacts from the state produced by the run.
Fork from any useful checkpoint to try alternatives, compare outcomes, and continue from the best branch.
## Use cases
Let AI agents execute and test code safely. Branch to explore multiple approaches, evaluate results, and continue the most promising path.
Run experiments in isolated environments. Fork state to test variations without starting over.
Provide students with safe code execution environments. Automatic cleanup and resource limits help prevent abuse.
Execute build steps and tests in isolated sandboxes with full resource tracking and artifact retrieval.
**Beta limitations**
* Number of simultaneously running operations is limited to 50.
* Checkpoint images retention is set to 180 days. After this period we may delete untagged unreferenced images (those that do not produce any other images).
Please contact us if you need those limitations lifted for the beta period.
## Resources
* [Sandboxes for SWE agents](/sandboxes/swe-agents) — Preloaded environments and Hugging Face datasets
* [Contree SDK](/sandboxes/sdk) — Python SDK for programmatic access to the Sandboxes API
* [Contree CLI](/sandboxes/cli) — Terminal client for interactive and scripted sandbox workflows
* [Contree MCP](/sandboxes/mcp) — Model Context Protocol server for AI assistants
# Overview
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/index
[](https://pypi.org/project/contree-sdk/)
[](https://pypi.org/project/contree-sdk/)
ConTree is a container runtime, providing **reproducible, versioned filesystem state** — like Git for container execution. The SDK makes this accessible from Python.
## Quick Start
### Installation
Install the SDK from PyPi:
```bash theme={null}
pip install contree-sdk
```
### Basic Usage
```python theme={null}
image = await client.images.use("busybox:latest")
print(f"Using {image=}")
result = await image.run(shell="echo 'Hello World'")
print(f"Simple echo: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
result = await image.run(shell="pwd")
print(f"Current directory: {result.stdout=}, {result.exit_code=}")
result = await image.run(shell="ls -la")
print(f"Directory listing: {result.stdout=}, {result.exit_code=}")
result = await image.run(shell="cat -", stdin="Hello from stdin\n")
print(f"Cat with stdin: {result.stdout=}, {result.exit_code=}")
result = await image.run(shell="echo 'Error message' >&2; exit 1")
print(f"Error command: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
```
```python theme={null}
image = client.images.use("busybox:latest")
print(f"Using {image=}")
result = image.run(shell="echo 'Hello World'").wait()
print(f"Simple echo: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
result = image.run(shell="pwd").wait()
print(f"Current directory: {result.stdout=}, {result.exit_code=}")
result = image.run(shell="ls -la").wait()
print(f"Directory listing: {result.stdout=}, {result.exit_code=}")
result = image.run(shell="cat -", stdin="Hello from stdin\n").wait()
print(f"Cat with stdin: {result.stdout=}, {result.exit_code=}")
result = image.run(shell="echo 'Error message' >&2; exit 1").wait()
print(f"Error command: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
```
## What’s Next?
Ready to explore more? Check out our guides:
Detailed setup and basic operations.
Pull and import container images.
Comprehensive guide to command execution.
Create reproducible execution branches.
# Integrations
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/integrations/index
# LangChain Integration
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/integrations/langchain
[LangChain](https://python.langchain.com/) is a popular framework for building AI agents.
The ConTree integration provides a `ContreeSandbox` backend that lets LangChain agents execute code in isolated, reproducible containers.
Integration is available via `contree-sdk[langchain]`, which provides `ContreeSandbox` — an implementation of [`BaseSandbox`](https://docs.langchain.com/oss/python/deepagents/backends/sandbox) from the [deepagents](https://pypi.org/project/deepagents/) package.
## Using ContreeSandbox
```python theme={null}
import asyncio
import os
from deepagents import create_deep_agent
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from pydantic import SecretStr
from contree_sdk import Contree
from contree_sdk.langchain.sandbox import ContreeSandbox
async def main():
client = Contree()
image = await client.images.oci("python:3.13-slim")
session = image.session()
sandbox = ContreeSandbox(session=session)
model = ChatOpenAI(
model="zai-org/GLM-5.2",
base_url="https://api.studio.nebius.ai/v1/",
api_key=SecretStr(os.environ["NEBIUS_API_KEY"]),
)
agent = create_deep_agent(model=model, backend=sandbox)
result = await agent.ainvoke({"messages": [HumanMessage("Develop a small calculator script and run it")]})
print(result["messages"][-1].content)
if __name__ == "__main__":
asyncio.run(main())
```
## Setup
1. Install the dependencies:
```bash theme={null}
pip install "contree-sdk[langchain]"
```
2. Set up Nebius IAM token and base URL:
```bash theme={null}
export NEBIUS_API_KEY="your-nebius-iam-token"
export NEBIUS_PROJECT_ID="your-project-id"
```
3. Set up your LLM provider credentials (e.g. for Anthropic):
```bash theme={null}
export ANTHROPIC_API_KEY="your-anthropic-api-key"
```
# Mini-SWE-Agent Integration
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/integrations/mini-swe-agent
[Mini-SWE-Agent](https://mini-swe-agent.com/latest/) is a lightweight software engineering agent.
The ConTree integration enables it to execute code in isolated, reproducible containers. Every command in Mini-SWE-Agent is executed in a fresh shell session, which makes it perfectly suitable for ConTree.
Integration is available via [ContreeEnvironment](https://mini-swe-agent.com/latest/reference/environments/contree/) starting from [mini-swe-agent v2.2.0](https://github.com/SWE-agent/mini-swe-agent/releases/tag/v2.2.0).
## Using ContreeEnvironment
```python theme={null}
from minisweagent.agents.default import DefaultAgent
from minisweagent.environments.extra.contree import ContreeEnvironment
from minisweagent.models import get_model
from contree_sdk.auth import IAMAuth
from contree_sdk.config import ContreeConfig
def main():
contree_env = ContreeEnvironment(
contree_config=ContreeConfig(
auth=IAMAuth(base_url="https://contree.dev/"),
),
image="python:3.13-slim",
cwd="/workspace",
)
agent = DefaultAgent(
get_model(input_model_name="gemini/gemini-flash-latest"), contree_env, system_template="", instance_template=""
)
agent.run("Develop small calculator script and check it")
result = contree_env.session.run(shell="ls /workspace -lah").wait()
print(result.stdout)
if __name__ == "__main__":
main()
```
## Running with SWE-bench
### Setup
1. Install the dependencies:
```bash theme={null}
pip install "mini-swe-agent[contree]"
```
2. Set up Nebius IAM token and base URL:
```bash theme={null}
export NEBIUS_API_KEY="your-nebius-iam-token"
export NEBIUS_PROJECT_ID="your-project-id"
export CONTREE_BASE_URL="your-given-base-url-for-contree"
```
### Usage
Run mini-swe-agent like with any other environment:
```bash theme={null}
mini-extra swebench \
--subset verified \
--split test \
--workers 100
--environment-class contree
```
It can be specified both through cli parameter or by setting `environment_class` to `contree` in your swebench.yaml config
# Branching Workflows
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/branching
One of ConTree’s most powerful features is the ability to branch execution flows from a single base filesystem state (a snapshot of all files and directories in a container at a specific point in time). This is similar to Git branches, but for container execution states.
## Why Branching Matters
Without ConTree, running the same operations twice (like installing packages, creating files, or compiling code) requires rebuilding the entire filesystem state from scratch each time. ConTree captures the exact filesystem state after each command, making it reproducible and allowing you to branch from that exact state.
## Simple Branching Example
This example demonstrates creating a parent state with a random value, then branching into multiple child states from that fixed parent:
```python theme={null}
base = await client.images.use("alpine:latest")
child = await base.run(shell='echo "$RANDOM" > /tmp/random.txt', disposable=False)
print(f"Child created from base, UUID: {child.uuid}\n")
for i, letter in enumerate(["A", "B", "C"], 1):
gc = await child.run(
shell=f"echo '{letter}' >> /tmp/random.txt && cat /tmp/random.txt",
disposable=False,
)
print(f"Grandchild {i}: {gc.stdout.strip()}")
```
See [`run()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImage.run) for full API reference.
```python theme={null}
base = client.images.use("alpine:latest")
child = base.run(shell='echo "$RANDOM" > /tmp/random.txt', disposable=False).wait()
print(f"Child created from base, UUID: {child.uuid}\n")
for i, letter in enumerate(["A", "B", "C"], 1):
gc = child.run(
shell=f"echo '{letter}' >> /tmp/random.txt && cat /tmp/random.txt",
disposable=False,
).wait()
print(f"Grandchild {i}: {gc.stdout.strip()}")
```
See [`run()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImageSync.run) for full API reference.
**Key Points:**
* The `child` state contains a random value that would be different on each execution without ConTree
* All three grandchildren start from the exact same `child` state (same random value)
* Each grandchild branches independently, creating different execution paths
## Advanced Branching Patterns
For more complex scenarios with multiple branching strategies:
```python theme={null}
image = await client.images.use("alpine:latest")
print(f"Using {image=}")
print("\nExample 1: Different commands from same image")
result1 = await image.run(shell="echo 'First branch'", disposable=False)
result2 = await image.run(shell="echo 'Second branch'", disposable=False)
result3 = await image.run(shell="ls /bin | head -3", disposable=False)
print(f"Branch 1: {result1.stdout=}, {result1.uuid=}")
print(f"Branch 2: {result2.stdout=}, {result2.uuid=}")
print(f"Branch 3: {result3.stdout=}, {result3.uuid=}")
print("\nExample 2: Random output command (different each time)")
random1 = await image.run(shell="od -An -N2 -tu2 /dev/urandom", disposable=False)
random2 = await image.run(shell="od -An -N2 -tu2 /dev/urandom", disposable=False)
print(f"Random 1: {random1.stdout=}, {random1.uuid=}")
print(f"Random 2: {random2.stdout=}, {random2.uuid=}")
print("\nExample 3: Chain of operations from different branches")
base_result = await image.run(shell="echo 'apple\nbanana\ncherry' > /tmp/fruits.txt", disposable=False)
sort_result = await base_result.run(shell="sort /tmp/fruits.txt", disposable=False)
reverse_result = await base_result.run(shell="sort -r /tmp/fruits.txt", disposable=False)
print(f"Base: {base_result.uuid=}")
print(f"Sorted: {sort_result.stdout=}, {sort_result.uuid=}")
print(f"Reverse sorted: {reverse_result.stdout=}, {reverse_result.uuid=}")
print("\nExample 4: Same command twice - same UUID")
same1 = await image.run(shell="echo 'Same command'", disposable=False)
same2 = await image.run(shell="echo 'Same command'", disposable=False)
print(f"Same 1: {same1.stdout=}, {same1.uuid=}")
print(f"Same 2: {same2.stdout=}, {same2.uuid=}")
print(f"UUIDs equal: {same1.uuid == same2.uuid}")
```
See [`ContreeImage`](./reference/image#contree_sdk.sdk.objects.image.ContreeImage) for full API reference.
```python theme={null}
image = client.images.use("alpine:latest")
print(f"Using {image=}")
print("\nExample 1: Different commands from same image")
result1 = image.run(shell="echo 'First branch'", disposable=False).wait()
result2 = image.run(shell="echo 'Second branch'", disposable=False).wait()
result3 = image.run(shell="ls /bin | head -3", disposable=False).wait()
print(f"Branch 1: {result1.stdout=}, {result1.uuid=}")
print(f"Branch 2: {result2.stdout=}, {result2.uuid=}")
print(f"Branch 3: {result3.stdout=}, {result3.uuid=}")
print("\nExample 2: Random output command (different each time)")
random1 = image.run(shell="od -An -N2 -tu2 /dev/urandom", disposable=False).wait()
random2 = image.run(shell="od -An -N2 -tu2 /dev/urandom", disposable=False).wait()
print(f"Random 1: {random1.stdout=}, {random1.uuid=}")
print(f"Random 2: {random2.stdout=}, {random2.uuid=}")
print("\nExample 3: Chain of operations from different branches")
base_result = image.run(shell="echo 'apple\nbanana\ncherry' > /tmp/fruits.txt", disposable=False).wait()
sort_result = base_result.run(shell="sort /tmp/fruits.txt", disposable=False).wait()
reverse_result = base_result.run(shell="sort -r /tmp/fruits.txt", disposable=False).wait()
print(f"Base: {base_result.uuid=}")
print(f"Sorted: {sort_result.stdout=}, {sort_result.uuid=}")
print(f"Reverse sorted: {reverse_result.stdout=}, {reverse_result.uuid=}")
print("\nExample 4: Same command twice - same UUID")
same1 = image.run(shell="echo 'Same command'", disposable=False).wait()
same2 = image.run(shell="echo 'Same command'", disposable=False).wait()
print(f"Same 1: {same1.stdout=}, {same1.uuid=}")
print(f"Same 2: {same2.stdout=}, {same2.uuid=}")
print(f"UUIDs equal: {same1.uuid == same2.uuid}")
```
See [`ContreeImageSync`](./reference/image#contree_sdk.sdk.objects.image.ContreeImageSync) for full API reference.
## Use Cases
Branching is particularly useful for:
* **Testing multiple scenarios**: Run different operations from the same starting state
* **Reproducible randomness**: Capture random/non-deterministic operations and branch from them
* **Parallel execution paths**: Execute different workflows from a common checkpoint
* **Version control for execution**: Create branches like Git for different execution flows
# Getting Started
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/getting-started
This guide will help you get up and running with ConTree SDK. By the end of this guide, you’ll understand how to create clients, work with images, and run your first commands.
## Configuration
The SDK resolves credentials in the following priority order:
1. **Explicit values** passed to `IAMAuth` / `JWTAuth` constructors via `ContreeConfig`.
2. **Environment variables**:
* `NEBIUS_API_KEY` — IAM token
* `NEBIUS_PROJECT_ID` — ConTree project ID
* `CONTREE_BASE_URL` — ConTree instance URL (for `JWTAuth`)
3. **`auth.ini`** — if the `contree` CLI is installed, credentials written by `contree auth` are read automatically from `~/.config/contree/auth.ini`.
```bash theme={null}
export NEBIUS_API_KEY="your_token_here"
export NEBIUS_PROJECT_ID="your_project_id"
export CONTREE_BASE_URL="https://your-instance.of.contree"
```
The active `auth.ini` profile defaults to `default` and can be overridden with `CONTREE_PROFILE`. The config directory respects `$CONTREE_HOME` and `$XDG_CONFIG_HOME`.
Alternatively, you can pass auth directly when creating a client via `ContreeConfig(auth=IAMAuth(...))` or use `JWTAuth` for legacy token-based access.
## Creating a Client
The first step is to create a ConTree client. You can choose between async and sync versions depending on your application needs.
Here’s how to create a client and verify the connection by listing available images:
```python theme={null}
# Get client
client = Contree()
# Get images (to verify that connection works)
await client.images()
```
See [`Contree`](./reference/client#contree_sdk.Contree) for all client options.
```python theme={null}
# Get client
client = ContreeSync()
# Get images (to verify that connection works)
client.images()
```
See [`ContreeSync`](./reference/client#contree_sdk.ContreeSync) for all client options.
## Working with Images
Images are the foundation of ConTree. The simplest way to reference an image is by tag using `images.use()`, which creates an image object without making an API call:
```python theme={null}
image = await contree.images.use("ubuntu:latest")
result = await image.run(shell="echo hello")
```
```python theme={null}
image = contree.images.use("ubuntu:latest")
result = image.run(shell="echo hello").wait()
```
To resolve a tag or UUID upfront via an API call, use `images.use(strict=True)`. To import from an external registry (or return an existing image if already imported), use `images.oci()`.
See [Working with Images](./images) for a full overview of available methods, examples, and what you can pass as a reference.
## Running Commands
Once you have an image, you can run commands inside it. Each command execution creates a new version of the image with your changes.
### Basic Command Execution
You can run various shell commands and handle their output:
```python theme={null}
image = await client.images.use("busybox:latest")
print(f"Using {image=}")
result = await image.run(shell="echo 'Hello World'")
print(f"Simple echo: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
result = await image.run(shell="pwd")
print(f"Current directory: {result.stdout=}, {result.exit_code=}")
result = await image.run(shell="ls -la")
print(f"Directory listing: {result.stdout=}, {result.exit_code=}")
result = await image.run(shell="cat -", stdin="Hello from stdin\n")
print(f"Cat with stdin: {result.stdout=}, {result.exit_code=}")
result = await image.run(shell="echo 'Error message' >&2; exit 1")
print(f"Error command: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
```
See [`run()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImage.run) for all command execution options.
```python theme={null}
image = client.images.use("busybox:latest")
print(f"Using {image=}")
result = image.run(shell="echo 'Hello World'").wait()
print(f"Simple echo: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
result = image.run(shell="pwd").wait()
print(f"Current directory: {result.stdout=}, {result.exit_code=}")
result = image.run(shell="ls -la").wait()
print(f"Directory listing: {result.stdout=}, {result.exit_code=}")
result = image.run(shell="cat -", stdin="Hello from stdin\n").wait()
print(f"Cat with stdin: {result.stdout=}, {result.exit_code=}")
result = image.run(shell="echo 'Error message' >&2; exit 1").wait()
print(f"Error command: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
```
See [`run()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImageSync.run) for all command execution options.
### Understanding the Results
When you run a command, you get back a result object that contains:
* **`stdout`**: Standard output from the command
* **`stderr`**: Standard error from the command
* **`exit_code`**: The exit code (0 for success, non-zero for errors)
* **`uuid`**: The UUID of the new image version created by this command
# Working with Images
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/images
ConTree SDK provides several ways to reference and import container images. For full API documentation, see [`ImagesManager`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager) and [`ImagesManagerSync`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync).
## Using Images by Tag
The simplest way to get an image is `images.use(tag)`. This creates an image object immediately without any API call — the tag is resolved at execution time when you run a command:
```python theme={null}
image = await contree.images.use("ubuntu:latest")
result = await image.run(shell="echo hello")
```
```python theme={null}
image = contree.images.use("ubuntu:latest")
result = image.run(shell="echo hello").wait()
```
## Pulling Images
For resolving a tag/UUID to an image upfront, use `images.use(strict=True)`. For importing images from external registries, use `images.oci()`:
```python theme={null}
print(f"Selected {image_uuid=}")
print(f"Selected {image_tag=}")
print("\nPulling by UUID (strict):")
result = await client.images.use(image_uuid, strict=True)
print(f"Pulled by UUID: {result.uuid=}, {result.tag=}, {result.state=}")
print("\nPulling by tag (strict):")
result = await client.images.use(image_tag, strict=True)
print(f"Pulled by tag: {result.uuid=}, {result.tag=}, {result.state=}")
print("\nImporting public image using oci:")
result = await client.images.oci("docker://ghcr.io/linuxserver/code-server:latest")
print(f"Pulled public: {result.uuid=}, {result.tag=}, {result.state=}")
```
See [`use()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager.use) and [`oci()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager.oci) for all parameters.
```python theme={null}
print(f"Selected {image_uuid=}")
print(f"Selected {image_tag=}")
print("\nPulling by UUID (strict):")
result = client.images.use(image_uuid, strict=True)
print(f"Pull by UUID: {result.uuid=}, {result.tag=}, {result.state=}")
print("\nPulling by tag (strict):")
result = client.images.use(image_tag, strict=True)
print(f"Pull by tag: {result.uuid=}, {result.tag=}, {result.state=}")
print("\nImporting public image using oci:")
result = client.images.oci("docker://ghcr.io/linuxserver/code-server:latest")
print(f"Import public: {result.uuid=}, {result.tag=}, {result.state=}")
```
See [`use()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync.use) and [`oci()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync.oci) for all parameters.
### Methods
* [`use()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager.use)(ref) — no API call; tag or UUID is resolved at execution time
* [`use()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager.use)(ref, strict=True) — verifies the image exists via an API call
* [`oci()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager.oci)(ref) (aliases: [`docker()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager.docker), [`podman()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager.podman), [`pull_by_oci()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager.pull_by_oci)) — like `use(strict=True)`, but imports from the registry if not found locally
* [`import_from()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager.import_from)(ref) — always imports from an external registry
* [`use()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync.use)(ref) — no API call; tag or UUID is resolved at execution time
* [`use()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync.use)(ref, strict=True) — verifies the image exists via an API call
* [`oci()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync.oci)(ref) (aliases: [`docker()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync.docker), [`podman()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync.podman), [`pull_by_oci()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync.pull_by_oci)) — like `use(strict=True)`, but imports from the registry if not found locally
* [`import_from()`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync.import_from)(ref) — always imports from an external registry
`import_from` always triggers a new import operation and should only be used when you explicitly need to re-import. In most cases, prefer `images.oci()`, which returns an existing image if already imported. If no import is needed at all, use `images.use()`.
### What `ref` can be
* UUID — reference an existing image by its UUID, e.g. `"550e8400-e29b-41d4-a716-446655440000"` or `UUID(...)`
* OCI tag — reference by image tag, e.g. `"ubuntu:latest"`
* OCI full URL — full reference including registry host, e.g. `"docker://ghcr.io/owner/image:tag"`
* [`OCIReference`](./reference/oci#contree_sdk.utils.oci.OCIReference) — programmatic OCI reference object
## Tagging Images
You can assign or remove a tag on any image using `tag_as()` and `untag()`. Tags are unique across all images — assigning an existing tag to a new image moves it automatically.
```python theme={null}
image = await client.images.use(image_tag, strict=True)
print(f"Original: {image.uuid=}, {image.tag=}")
tagged = await image.tag_as("my-custom-tag:v1")
print(f"After tag_as: {tagged.uuid=}, {tagged.tag=}")
untagged = await tagged.untag()
print(f"After untag: {untagged.uuid=}, {untagged.tag=}")
result = await image.run(shell="echo hello", tag="my-result:v1", disposable=False)
print(f"Run result: {result.uuid=}, {result.tag=}")
```
See [`tag_as()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImage.tag_as) and [`untag()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImage.untag) for details.
```python theme={null}
image = client.images.use(image_tag, strict=True)
print(f"Original: {image.uuid=}, {image.tag=}")
tagged = image.tag_as("my-custom-tag:v1")
print(f"After tag_as: {tagged.uuid=}, {tagged.tag=}")
untagged = tagged.untag()
print(f"After untag: {untagged.uuid=}, {untagged.tag=}")
result = image.run(shell="echo hello", tag="my-result:v1", disposable=False).wait()
print(f"Run result: {result.uuid=}, {result.tag=}")
```
See [`tag_as()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImageSync.tag_as) and [`untag()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImageSync.untag) for details.
You can also tag the result of a `run()` directly by passing `tag=` to the call — the resulting image will be tagged after execution completes:
```python theme={null}
result = await image.run(shell="pip install mylib && python setup.py", tag="myapp:ready", disposable=False)
print(result.tag) # "myapp:ready"
```
```python theme={null}
result = image.run(shell="pip install mylib && python setup.py", tag="myapp:ready", disposable=False).wait()
print(result.tag) # "myapp:ready"
```
## Listing Images
View all available images in your ConTree instance:
```python theme={null}
all_images = await client.images()
print(f"Loaded {all_images=}")
limited_images = await client.images(number=3)
print(f"Found {len(limited_images)=}")
tagged_images = await client.images(tagged=True)
print(f"Found {len(tagged_images)=}")
imported_images = await client.images(kind=ImageKind.IMPORTED)
print(f"Found {len(imported_images)=}")
recent_images = await client.images(since=datetime.now() - timedelta(days=7), number=5)
print(f"Found {len(recent_images)=}")
```
See [`ImagesManager`](./reference/images#contree_sdk.sdk.managers.images.ImagesManager) for filtering and iteration options.
```python theme={null}
all_images = client.images()
print(f"Loaded {all_images=}")
limited_images = client.images(number=3)
print(f"Found {len(limited_images)=}")
tagged_images = client.images(tagged=True)
print(f"Found {len(tagged_images)=}")
imported_images = client.images(kind=ImageKind.IMPORTED)
print(f"Found {len(imported_images)=}")
recent_images = client.images(since=datetime.now() - timedelta(days=7), number=5)
print(f"Found {len(recent_images)=}")
```
See [`ImagesManagerSync`](./reference/images#contree_sdk.sdk.managers.images.ImagesManagerSync) for filtering and iteration options.
# Auth
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/auth
## `class Auth`
```python theme={null}
class contree_sdk.auth.Auth(base_url)
```
### Attributes
API server URL or env var name to load from.
### Methods
#### `resolve()`
```python theme={null}
def resolve() -> Self
```
#### `get_headers()`
```python theme={null}
@abstractmethod
def get_headers() -> dict[str, str]
```
## `class JWTAuth`
```python theme={null}
class contree_sdk.auth.JWTAuth(base_url, token)
```
### Attributes
Auth token or env var name to load from.
API server URL or env var name to load from.
### Methods
#### `get_headers()`
```python theme={null}
def get_headers() -> dict[str, str]
```
#### `resolve()`
```python theme={null}
def resolve() -> Self
```
## `class IAMAuth`
```python theme={null}
class contree_sdk.auth.IAMAuth(base_url, token, project_id)
```
### Attributes
IAM token or env var name to load from.
Nebius project ID or env var name to load from.
API server URL. Defaults to the Nebius Token Factory production endpoint.
### Methods
#### `resolve()`
```python theme={null}
def resolve() -> Self
```
#### `get_headers()`
```python theme={null}
def get_headers() -> dict[str, str]
```
# Clients
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/client
## `class Contree`
```python theme={null}
class contree_sdk.Contree(config, *, base_url, token)
```
Asynchronous ConTree SDK client.
### Attributes
[`ContreeConfig`](./config#contree_sdk.config.ContreeConfig)
Current client configuration.
[`FilesManager`](./files#contree_sdk.sdk.managers.files.FilesManager)
Manager for file operations.
[`ImagesManager`](./images#contree_sdk.sdk.managers.images.ImagesManager)
Manager for image operations.
### Methods
#### `get_token_info()`
```python theme={null}
async def get_token_info(refresh) -> WhoAmI
```
## `class ContreeSync`
```python theme={null}
class contree_sdk.ContreeSync(config, *, base_url, token)
```
Synchronous ConTree SDK client.
### Attributes
[`ContreeConfig`](./config#contree_sdk.config.ContreeConfig)
Current client configuration.
[`ImagesManagerSync`](./images#contree_sdk.sdk.managers.images.ImagesManagerSync)
Manager for image operations.
[`FilesManagerSync`](./files#contree_sdk.sdk.managers.files.FilesManagerSync)
Manager for file operations.
### Methods
#### `get_token_info()`
```python theme={null}
def get_token_info(refresh) -> WhoAmI
```
# ContreeConfig
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/config
## `class ContreeConfig`
```python theme={null}
class contree_sdk.config.ContreeConfig(auth, transport_timeout, file_upload_chunk_size, operation_import_timeout, operation_run_timeout, operation_timeout, default_truncate_output_at, token_expiration_warning_threshold, images_list_batch_size)
```
Configuration for the ConTree SDK client.
The `auth` field controls authentication and the target URL. String fields
on auth objects support env var lookup: if the value matches an existing
environment variable name, the value is loaded from it.
### Attributes
[`IAMAuth`](./auth#contree_sdk.auth.IAMAuth) | [`JWTAuth`](./auth#contree_sdk.auth.JWTAuth)
Authentication configuration. Use `IAMAuth` for Nebius IAM tokens or `JWTAuth` for legacy tokens.
HTTP timeout in seconds.
Chunk size in bytes for uploads.
Import operation timeout, falls back to operation\_timeout.
Run operation timeout, falls back to operation\_timeout.
Default timeout for operations.
Default truncate output at which to truncate stdout and stderr.
Warn if token expires within this duration.
Batch size for listing images.
# Files Manager
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/files
## `class FilesManager`
```python theme={null}
class contree_sdk.sdk.managers.files.FilesManager(client)
```
### Methods
#### `upload()`
```python theme={null}
async def upload(local_path) -> UploadedFile
```
## `class FilesManagerSync`
```python theme={null}
class contree_sdk.sdk.managers.files.FilesManagerSync(client)
```
### Methods
#### `upload()`
```python theme={null}
def upload(local_path) -> UploadedFile
```
# Images
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/image
## `class ContreeImage`
```python theme={null}
class contree_sdk.sdk.objects.image.ContreeImage(client, uuid, tag)
```
### Attributes
Time elapsed during execution.
Exit code of the executed command.
Execution result. Only available after successful execution.
Current state of the image in the execution lifecycle.
Stderr output from the execution.
Configured stdin source.
Stdout output from the execution.
Unique identifier of the image.
Optional tag associated with the image.
### Methods
#### `apply_files()`
```python theme={null}
async def apply_files(*args, files) -> TypeVar(_T, bound= _ImageLikeBase)
```
Upload files into a new image derived from this one.
Files to upload.
Files as a list or a dict mapping destination paths to sources.
When both args and files are provided, they are merged.
New image with the uploaded files baked in.
#### `download()`
```python theme={null}
async def download(image_path, local_path) -> Path | None
```
Download a file from the image to local filesystem.
Path to the file inside the image.
Local destination path. Defaults to filename from image\_path.
Path to the downloaded file.
#### `ls()`
```python theme={null}
async def ls(path) -> list[ImageFile | ImageDirectory]
```
List files and directories at the given path.
Path inside the image to list.
list\[[`ImageFile`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageFile) | [`ImageDirectory`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageDirectory)]
List of ImageFile and ImageDirectory objects.
#### `read()`
```python theme={null}
async def read(image_path) -> bytes
```
Read file contents from the image.
Path to the file inside the image.
File contents as bytes.
#### `run()`
```python theme={null}
def run(command, *, shell, args, env, cwd, hostname, stdin, stdout, stderr, tag, files, timeout, disposable, truncate_output_at, preserve_env) -> TypeVar(_T, bound= _ImageLikeBase)
```
Prepare image for command execution.
Command to execute (mutually exclusive with shell).
Shell command string (mutually exclusive with command).
Command arguments.
Environment variables.
Working directory inside the image.
Hostname for the container.
Input source.
Output destination for stdout.
Output destination for stderr.
Tag for the resulting image.
Files to upload into the image.
Execution timeout in seconds or as timedelta.
If True, image is discarded after execution.
number of bytes to truncate stdout and stderr. Defaults to default\_truncate\_output\_at
If True, environment variables are preserved in resulting image after execution.
New image instance configured for execution.
**DisposableImageRunError** – If attempting to run on a disposed image.
**ValueError** – If neither command nor shell is provided.
#### `session()`
```python theme={null}
def session() -> ContreeSession
```
[`ContreeSession`](./session#contree_sdk.sdk.objects.session.ContreeSession)
#### `start()`
```python theme={null}
async def start() -> TypeVar(_T, bound= _ImageLikeBase)
```
Start the prepared command without waiting for completion.
New image instance in EXECUTING state; await it or iterate its
output chunks to get the result.
#### `tag_as()`
```python theme={null}
async def tag_as(tag) -> TypeVar(_T, bound= _ImageLikeBase)
```
Tag this image with the specified tag, or remove the tag if None.
Tag name to apply to the image, or None to remove the tag.
New instance with updated tag.
#### `untag()`
```python theme={null}
async def untag() -> TypeVar(_T, bound= _ImageLikeBase)
```
Remove the tag from this image.
New instance with tag set to None.
## `class ContreeImageSync`
```python theme={null}
class contree_sdk.sdk.objects.image.ContreeImageSync(client, uuid, tag)
```
### Attributes
Time elapsed during execution.
Exit code of the executed command.
Execution result. Only available after successful execution.
Current state of the image in the execution lifecycle.
Stderr output from the execution.
Configured stdin source.
Stdout output from the execution.
Unique identifier of the image.
Optional tag associated with the image.
### Methods
#### `apply_files()`
```python theme={null}
def apply_files(*args, files) -> TypeVar(_T, bound= _ImageLikeBase)
```
Upload files into a new image derived from this one.
Files to upload.
Files as a list or a dict mapping destination paths to sources.
When both args and files are provided, they are merged.
New image with the uploaded files baked in.
#### `download()`
```python theme={null}
def download(image_path, local_path) -> Path | None
```
Download a file from the image to local filesystem.
Path to the file inside the image.
Local destination path. Defaults to filename from image\_path.
Path to the downloaded file.
#### `ls()`
```python theme={null}
def ls(path) -> list[ImageFileSync | ImageDirectorySync]
```
List files and directories at the given path.
Path inside the image to list.
list\[[`ImageFileSync`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageFileSync) | [`ImageDirectorySync`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageDirectorySync)]
List of ImageFileSync and ImageDirectorySync objects.
#### `popen()`
```python theme={null}
def popen(args, *, stdin, input, stdout, stderr, shell, cwd, timeout, check, text, env) -> ContreeProcessSync
```
Run a command with subprocess-like interface.
Command and arguments list.
Input source.
Alternative input source (alias for stdin).
Output destination for stdout.
Output destination for stderr.
If True, treat args as shell command.
Working directory inside the image.
Execution timeout in seconds.
If True, raise on non-zero exit code.
If True, decode output as text.
Environment variables.
[`ContreeProcessSync`](./subprocess#contree_sdk.sdk.objects.subprocess.ContreeProcessSync)
ContreeProcessSync object with execution results.
#### `read()`
```python theme={null}
def read(image_path) -> bytes
```
Read file contents from the image.
Path to the file inside the image.
File contents as bytes.
#### `run()`
```python theme={null}
def run(command, *, shell, args, env, cwd, hostname, stdin, stdout, stderr, tag, files, timeout, disposable, truncate_output_at, preserve_env) -> TypeVar(_T, bound= _ImageLikeBase)
```
Prepare image for command execution.
Command to execute (mutually exclusive with shell).
Shell command string (mutually exclusive with command).
Command arguments.
Environment variables.
Working directory inside the image.
Hostname for the container.
Input source.
Output destination for stdout.
Output destination for stderr.
Tag for the resulting image.
Files to upload into the image.
Execution timeout in seconds or as timedelta.
If True, image is discarded after execution.
number of bytes to truncate stdout and stderr. Defaults to default\_truncate\_output\_at
If True, environment variables are preserved in resulting image after execution.
New image instance configured for execution.
**DisposableImageRunError** – If attempting to run on a disposed image.
**ValueError** – If neither command nor shell is provided.
#### `session()`
```python theme={null}
def session() -> ContreeSessionSync
```
[`ContreeSessionSync`](./session#contree_sdk.sdk.objects.session.ContreeSessionSync)
#### `start()`
```python theme={null}
def start() -> TypeVar(_T, bound= _ImageLikeBase)
```
Start the prepared command without waiting for completion.
New image instance in EXECUTING state; await it or iterate its
output chunks to get the result.
#### `tag_as()`
```python theme={null}
def tag_as(tag) -> TypeVar(_T, bound= _ImageLikeBase)
```
Tag this image with the specified tag, or remove the tag if None.
Tag name to apply to the image, or None to remove the tag.
New instance with updated tag.
#### `untag()`
```python theme={null}
def untag() -> TypeVar(_T, bound= _ImageLikeBase)
```
Remove the tag from this image.
New instance with tag set to None.
#### `wait()`
```python theme={null}
def wait() -> TypeVar(_T, bound= _ImageLikeSync)
```
Execute the prepared command and wait for completion.
New image instance with execution results.
# Image files/directories
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/image_fs
## `class ImageDirectory`
```python theme={null}
class contree_sdk.sdk.objects.image_fs.ImageDirectory(size, path, uid, gid, mode, mtime, nlink, symlink_to, is_dir, is_regular, is_socket, is_fifo, is_symlink, owner, group, _image, _path)
```
### Attributes
### Methods
#### `ls()`
```python theme={null}
async def ls(path) -> list[ImageFile | ImageDirectory]
```
list\[[`ImageFile`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageFile) | [`ImageDirectory`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageDirectory)]
## `class ImageDirectorySync`
```python theme={null}
class contree_sdk.sdk.objects.image_fs.ImageDirectorySync(size, path, uid, gid, mode, mtime, nlink, symlink_to, is_dir, is_regular, is_socket, is_fifo, is_symlink, owner, group, _image, _path)
```
### Attributes
### Methods
#### `ls()`
```python theme={null}
def ls(path) -> list[ImageFileSync | ImageDirectorySync]
```
list\[[`ImageFileSync`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageFileSync) | [`ImageDirectorySync`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageDirectorySync)]
## `class ImageFile`
```python theme={null}
class contree_sdk.sdk.objects.image_fs.ImageFile(size, path, uid, gid, mode, mtime, nlink, symlink_to, is_dir, is_regular, is_socket, is_fifo, is_symlink, owner, group, _image, _path)
```
### Attributes
### Methods
#### `download()`
```python theme={null}
async def download(local_path) -> Path
```
#### `read()`
```python theme={null}
async def read() -> bytes
```
## `class ImageFileSync`
```python theme={null}
class contree_sdk.sdk.objects.image_fs.ImageFileSync(size, path, uid, gid, mode, mtime, nlink, symlink_to, is_dir, is_regular, is_socket, is_fifo, is_symlink, owner, group, _image, _path)
```
### Attributes
### Methods
#### `download()`
```python theme={null}
def download(local_path) -> Path
```
#### `read()`
```python theme={null}
def read() -> bytes
```
# Images Manager
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/images
## `class ImagesManager`
```python theme={null}
class contree_sdk.sdk.managers.images.ImagesManager(client)
```
### Methods
#### `__aiter__()`
```python theme={null}
async def __aiter__()
```
#### `__call__()`
```python theme={null}
async def __call__(number, kind, tagged, since, until) -> list[TypeVar(_ImageT, bound= _ContreeImageBase)]
```
Fetch a list of images with optional filters.
Maximum number of images to return. None returns all.
Filter by image kind.
If True, return only tagged images.
Return images created after this time. Accepts datetime or timedelta relative to now.
Return images created before this time. Accepts datetime or timedelta relative to now.
List of images matching the given filters.
#### `docker()`
```python theme={null}
async def docker(ref, *, tag, username, password, timeout) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Resolve an image by tag, falling back to import if not found.
Derives the target tag from the `tag` parameter or from the reference itself,
then tries to find an existing image with that tag. If the image does not exist,
triggers an import and returns the result.
str | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference) | UUID
UUID, OCI reference string, or OCIReference of the image.
Tag override; if provided, replaces the tag from the reference.
Registry username for authenticated imports.
Registry password for authenticated imports.
Maximum seconds to wait for the import operation.
Resolved or imported image object.
**NotFoundError** – If ref is a UUID and the image does not exist.
#### `import_from()`
```python theme={null}
async def import_from(image, *, tag, username, password, timeout) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Import an image from an external registry into Contree.
str | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference)
OCI reference string or OCIReference pointing to the source image.
Tag to assign to the imported image. Defaults to the tag in the reference.
Registry username for private registries.
Registry password for private registries.
Maximum seconds to wait for the import operation.
Imported image object.
**ValueError** – If image is a UUID or credentials are incomplete.
**FailedOperationError** – If the import operation completes without returning an image.
#### `oci()`
```python theme={null}
async def oci(ref, *, tag, username, password, timeout) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Resolve an image by tag, falling back to import if not found.
Derives the target tag from the `tag` parameter or from the reference itself,
then tries to find an existing image with that tag. If the image does not exist,
triggers an import and returns the result.
str | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference) | UUID
UUID, OCI reference string, or OCIReference of the image.
Tag override; if provided, replaces the tag from the reference.
Registry username for authenticated imports.
Registry password for authenticated imports.
Maximum seconds to wait for the import operation.
Resolved or imported image object.
**NotFoundError** – If ref is a UUID and the image does not exist.
#### `podman()`
```python theme={null}
async def podman(ref, *, tag, username, password, timeout) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Resolve an image by tag, falling back to import if not found.
Derives the target tag from the `tag` parameter or from the reference itself,
then tries to find an existing image with that tag. If the image does not exist,
triggers an import and returns the result.
str | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference) | UUID
UUID, OCI reference string, or OCIReference of the image.
Tag override; if provided, replaces the tag from the reference.
Registry username for authenticated imports.
Registry password for authenticated imports.
Maximum seconds to wait for the import operation.
Resolved or imported image object.
**NotFoundError** – If ref is a UUID and the image does not exist.
#### `pull()`
```python theme={null}
async def pull(url_or_tag_or_uuid, *, new_tag, username, password, timeout) -> ContreeImage
```
[`ContreeImage`](./image#contree_sdk.sdk.objects.image.ContreeImage)
#### `pull_by_oci()`
```python theme={null}
async def pull_by_oci(ref, *, tag, username, password, timeout) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Resolve an image by tag, falling back to import if not found.
Derives the target tag from the `tag` parameter or from the reference itself,
then tries to find an existing image with that tag. If the image does not exist,
triggers an import and returns the result.
str | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference) | UUID
UUID, OCI reference string, or OCIReference of the image.
Tag override; if provided, replaces the tag from the reference.
Registry username for authenticated imports.
Registry password for authenticated imports.
Maximum seconds to wait for the import operation.
Resolved or imported image object.
**NotFoundError** – If ref is a UUID and the image does not exist.
#### `use()`
```python theme={null}
async def use(ref, strict) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Resolve a reference to an image object without importing.
str | UUID | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference)
Image identifier — UUID, OCI reference string, or OCIReference object.
If True, verify the image exists by fetching it from the API.
Image object corresponding to the given reference.
## `class ImagesManagerSync`
```python theme={null}
class contree_sdk.sdk.managers.images.ImagesManagerSync(client)
```
### Methods
#### `__call__()`
```python theme={null}
def __call__(number, kind, tagged, since, until) -> list[TypeVar(_ImageT, bound= _ContreeImageBase)]
```
Fetch a list of images with optional filters.
Maximum number of images to return. None returns all.
Filter by image kind.
If True, return only tagged images.
Return images created after this time. Accepts datetime or timedelta relative to now.
Return images created before this time. Accepts datetime or timedelta relative to now.
List of images matching the given filters.
#### `__iter__()`
```python theme={null}
def __iter__()
```
#### `docker()`
```python theme={null}
def docker(ref, *, tag, username, password, timeout) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Resolve an image by tag, falling back to import if not found.
Derives the target tag from the `tag` parameter or from the reference itself,
then tries to find an existing image with that tag. If the image does not exist,
triggers an import and returns the result.
str | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference) | UUID
UUID, OCI reference string, or OCIReference of the image.
Tag override; if provided, replaces the tag from the reference.
Registry username for authenticated imports.
Registry password for authenticated imports.
Maximum seconds to wait for the import operation.
Resolved or imported image object.
**NotFoundError** – If ref is a UUID and the image does not exist.
#### `import_from()`
```python theme={null}
def import_from(image, *, tag, username, password, timeout) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Import an image from an external registry into Contree.
str | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference)
OCI reference string or OCIReference pointing to the source image.
Tag to assign to the imported image. Defaults to the tag in the reference.
Registry username for private registries.
Registry password for private registries.
Maximum seconds to wait for the import operation.
Imported image object.
**ValueError** – If image is a UUID or credentials are incomplete.
**FailedOperationError** – If the import operation completes without returning an image.
#### `oci()`
```python theme={null}
def oci(ref, *, tag, username, password, timeout) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Resolve an image by tag, falling back to import if not found.
Derives the target tag from the `tag` parameter or from the reference itself,
then tries to find an existing image with that tag. If the image does not exist,
triggers an import and returns the result.
str | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference) | UUID
UUID, OCI reference string, or OCIReference of the image.
Tag override; if provided, replaces the tag from the reference.
Registry username for authenticated imports.
Registry password for authenticated imports.
Maximum seconds to wait for the import operation.
Resolved or imported image object.
**NotFoundError** – If ref is a UUID and the image does not exist.
#### `podman()`
```python theme={null}
def podman(ref, *, tag, username, password, timeout) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Resolve an image by tag, falling back to import if not found.
Derives the target tag from the `tag` parameter or from the reference itself,
then tries to find an existing image with that tag. If the image does not exist,
triggers an import and returns the result.
str | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference) | UUID
UUID, OCI reference string, or OCIReference of the image.
Tag override; if provided, replaces the tag from the reference.
Registry username for authenticated imports.
Registry password for authenticated imports.
Maximum seconds to wait for the import operation.
Resolved or imported image object.
**NotFoundError** – If ref is a UUID and the image does not exist.
#### `pull()`
```python theme={null}
def pull(url_or_tag_or_uuid, *, new_tag, username, password, timeout) -> ContreeImageSync
```
[`ContreeImageSync`](./image#contree_sdk.sdk.objects.image.ContreeImageSync)
#### `pull_by_oci()`
```python theme={null}
def pull_by_oci(ref, *, tag, username, password, timeout) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Resolve an image by tag, falling back to import if not found.
Derives the target tag from the `tag` parameter or from the reference itself,
then tries to find an existing image with that tag. If the image does not exist,
triggers an import and returns the result.
str | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference) | UUID
UUID, OCI reference string, or OCIReference of the image.
Tag override; if provided, replaces the tag from the reference.
Registry username for authenticated imports.
Registry password for authenticated imports.
Maximum seconds to wait for the import operation.
Resolved or imported image object.
**NotFoundError** – If ref is a UUID and the image does not exist.
#### `use()`
```python theme={null}
def use(ref, strict) -> TypeVar(_ImageT, bound= _ContreeImageBase)
```
Resolve a reference to an image object without importing.
str | UUID | [`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference)
Image identifier — UUID, OCI reference string, or OCIReference object.
If True, verify the image exists by fetching it from the API.
Image object corresponding to the given reference.
# API Reference
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/index
Complete API reference for ConTree SDK.
## Core Components
# OCIReference
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/oci
## `class OCIReference`
```python theme={null}
class contree_sdk.utils.oci.OCIReference(url, tag)
```
### Attributes
### Methods
#### `from_oci()`
```python theme={null}
@classmethod
def from_oci(ref) -> OCIReference
```
[`OCIReference`](./oci#contree_sdk.utils.oci.OCIReference)
# Sessions
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/session
## `class ContreeSession`
```python theme={null}
class contree_sdk.sdk.objects.session.ContreeSession(image)
```
### Attributes
Time elapsed during execution.
Exit code of the executed command.
Execution result. Only available after successful execution.
Current state of the image in the execution lifecycle.
Stderr output from the execution.
Configured stdin source.
Stdout output from the execution.
Unique identifier of the image.
Optional tag associated with the image.
### Methods
#### `apply_files()`
```python theme={null}
async def apply_files(*args, files) -> TypeVar(_T, bound= _ImageLikeBase)
```
Upload files into a new image derived from this one.
Files to upload.
Files as a list or a dict mapping destination paths to sources.
When both args and files are provided, they are merged.
New image with the uploaded files baked in.
#### `download()`
```python theme={null}
async def download(image_path, local_path) -> Path | None
```
Download a file from the image to local filesystem.
Path to the file inside the image.
Local destination path. Defaults to filename from image\_path.
Path to the downloaded file.
#### `ls()`
```python theme={null}
async def ls(path) -> list[ImageFile | ImageDirectory]
```
List files and directories at the given path.
Path inside the image to list.
list\[[`ImageFile`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageFile) | [`ImageDirectory`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageDirectory)]
List of ImageFile and ImageDirectory objects.
#### `read()`
```python theme={null}
async def read(image_path) -> bytes
```
Read file contents from the image.
Path to the file inside the image.
File contents as bytes.
#### `run()`
```python theme={null}
def run(command, *, shell, args, env, cwd, hostname, stdin, stdout, stderr, tag, files, timeout, disposable, truncate_output_at, preserve_env) -> TypeVar(_T, bound= _ImageLikeBase)
```
Prepare image for command execution.
Command to execute (mutually exclusive with shell).
Shell command string (mutually exclusive with command).
Command arguments.
Environment variables.
Working directory inside the image.
Hostname for the container.
Input source.
Output destination for stdout.
Output destination for stderr.
Tag for the resulting image.
Files to upload into the image.
Execution timeout in seconds or as timedelta.
If True, image is discarded after execution.
number of bytes to truncate stdout and stderr. Defaults to default\_truncate\_output\_at
If True, environment variables are preserved in resulting image after execution.
New image instance configured for execution.
**DisposableImageRunError** – If attempting to run on a disposed image.
**ValueError** – If neither command nor shell is provided.
#### `start()`
```python theme={null}
async def start() -> TypeVar(_T, bound= _ImageLikeBase)
```
Start the prepared command without waiting for completion.
New image instance in EXECUTING state; await it or iterate its
output chunks to get the result.
#### `tag_as()`
```python theme={null}
async def tag_as(tag) -> TypeVar(_T, bound= _ImageLikeBase)
```
Tag this image with the specified tag, or remove the tag if None.
Tag name to apply to the image, or None to remove the tag.
New instance with updated tag.
#### `untag()`
```python theme={null}
async def untag() -> TypeVar(_T, bound= _ImageLikeBase)
```
Remove the tag from this image.
New instance with tag set to None.
## `class ContreeSessionSync`
```python theme={null}
class contree_sdk.sdk.objects.session.ContreeSessionSync(image)
```
### Attributes
Time elapsed during execution.
Exit code of the executed command.
Execution result. Only available after successful execution.
Current state of the image in the execution lifecycle.
Stderr output from the execution.
Configured stdin source.
Stdout output from the execution.
Unique identifier of the image.
Optional tag associated with the image.
### Methods
#### `apply_files()`
```python theme={null}
def apply_files(*args, files) -> TypeVar(_T, bound= _ImageLikeBase)
```
Upload files into a new image derived from this one.
Files to upload.
Files as a list or a dict mapping destination paths to sources.
When both args and files are provided, they are merged.
New image with the uploaded files baked in.
#### `download()`
```python theme={null}
def download(image_path, local_path) -> Path | None
```
Download a file from the image to local filesystem.
Path to the file inside the image.
Local destination path. Defaults to filename from image\_path.
Path to the downloaded file.
#### `ls()`
```python theme={null}
def ls(path) -> list[ImageFileSync | ImageDirectorySync]
```
List files and directories at the given path.
Path inside the image to list.
list\[[`ImageFileSync`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageFileSync) | [`ImageDirectorySync`](./image_fs#contree_sdk.sdk.objects.image_fs.ImageDirectorySync)]
List of ImageFileSync and ImageDirectorySync objects.
#### `popen()`
```python theme={null}
def popen(args, *, stdin, input, stdout, stderr, shell, cwd, timeout, check, text, env) -> ContreeProcessSync
```
Run a command with subprocess-like interface.
Command and arguments list.
Input source.
Alternative input source (alias for stdin).
Output destination for stdout.
Output destination for stderr.
If True, treat args as shell command.
Working directory inside the image.
Execution timeout in seconds.
If True, raise on non-zero exit code.
If True, decode output as text.
Environment variables.
[`ContreeProcessSync`](./subprocess#contree_sdk.sdk.objects.subprocess.ContreeProcessSync)
ContreeProcessSync object with execution results.
#### `read()`
```python theme={null}
def read(image_path) -> bytes
```
Read file contents from the image.
Path to the file inside the image.
File contents as bytes.
#### `run()`
```python theme={null}
def run(command, *, shell, args, env, cwd, hostname, stdin, stdout, stderr, tag, files, timeout, disposable, truncate_output_at, preserve_env) -> TypeVar(_T, bound= _ImageLikeBase)
```
Prepare image for command execution.
Command to execute (mutually exclusive with shell).
Shell command string (mutually exclusive with command).
Command arguments.
Environment variables.
Working directory inside the image.
Hostname for the container.
Input source.
Output destination for stdout.
Output destination for stderr.
Tag for the resulting image.
Files to upload into the image.
Execution timeout in seconds or as timedelta.
If True, image is discarded after execution.
number of bytes to truncate stdout and stderr. Defaults to default\_truncate\_output\_at
If True, environment variables are preserved in resulting image after execution.
New image instance configured for execution.
**DisposableImageRunError** – If attempting to run on a disposed image.
**ValueError** – If neither command nor shell is provided.
#### `start()`
```python theme={null}
def start() -> TypeVar(_T, bound= _ImageLikeBase)
```
Start the prepared command without waiting for completion.
New image instance in EXECUTING state; await it or iterate its
output chunks to get the result.
#### `tag_as()`
```python theme={null}
def tag_as(tag) -> TypeVar(_T, bound= _ImageLikeBase)
```
Tag this image with the specified tag, or remove the tag if None.
Tag name to apply to the image, or None to remove the tag.
New instance with updated tag.
#### `untag()`
```python theme={null}
def untag() -> TypeVar(_T, bound= _ImageLikeBase)
```
Remove the tag from this image.
New instance with tag set to None.
#### `wait()`
```python theme={null}
def wait() -> TypeVar(_T, bound= _ImageLikeSync)
```
Execute the prepared command and wait for completion.
New image instance with execution results.
# subprocess
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/reference/subprocess
## `class ContreeProcessSync`
```python theme={null}
class contree_sdk.sdk.objects.subprocess.ContreeProcessSync(image, check)
```
### Attributes
### Methods
#### `communicate()`
```python theme={null}
def communicate(input, timeout)
```
#### `wait()`
```python theme={null}
def wait() -> None
```
# Running Commands
Source: https://docs.tokenfactory.nebius.com/sandboxes/sdk/python_sdk/running-commands
ConTree SDK provides multiple ways to execute commands in containers, from simple shell commands to complex workflows with file handling and custom I/O.
## Basic Command Execution
You can run commands using shell syntax or by specifying command and arguments separately:
```python theme={null}
image = await client.images.use("busybox:latest")
print(f"Using {image=}")
result = await image.run(shell="echo 'Hello World'")
print(f"Simple echo: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
result = await image.run(shell="pwd")
print(f"Current directory: {result.stdout=}, {result.exit_code=}")
result = await image.run(shell="ls -la")
print(f"Directory listing: {result.stdout=}, {result.exit_code=}")
result = await image.run(shell="cat -", stdin="Hello from stdin\n")
print(f"Cat with stdin: {result.stdout=}, {result.exit_code=}")
result = await image.run(shell="echo 'Error message' >&2; exit 1")
print(f"Error command: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
```
See [`ContreeImage.run()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImage.run) and [`ContreeSession.run()`](./reference/session#contree_sdk.sdk.objects.session.ContreeSession.run) for all options.
```python theme={null}
image = client.images.use("busybox:latest")
print(f"Using {image=}")
result = image.run(shell="echo 'Hello World'").wait()
print(f"Simple echo: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
result = image.run(shell="pwd").wait()
print(f"Current directory: {result.stdout=}, {result.exit_code=}")
result = image.run(shell="ls -la").wait()
print(f"Directory listing: {result.stdout=}, {result.exit_code=}")
result = image.run(shell="cat -", stdin="Hello from stdin\n").wait()
print(f"Cat with stdin: {result.stdout=}, {result.exit_code=}")
result = image.run(shell="echo 'Error message' >&2; exit 1").wait()
print(f"Error command: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
```
See [`ContreeImageSync.run()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImageSync.run) and [`ContreeSessionSync.run()`](./reference/session#contree_sdk.sdk.objects.session.ContreeSessionSync.run) for all options.
## Command Execution Mode
You can execute commands by specifying the executable path and arguments separately:
```python theme={null}
image = await client.images.use("alpine:3.20", strict=True)
print(f"Pulled {image=}")
print("\nExample 1: Simple command execution")
result = await image.run("/bin/echo", args=["Hello from command parameter!"])
print(f"Result: {result.stdout=}, {result.exit_code=}")
print("\nExample 2: Command with arguments")
result = await image.run("/bin/ls", args=["-la", "/tmp"])
print(f"Result: {result.stdout=}, {result.exit_code=}")
print("\nExample 3: Command with environment variables")
result = await image.run("/bin/printenv", args=["MY_VAR"], env={"MY_VAR": "test_value"})
print(f"Result: {result.stdout=}, {result.exit_code=}")
print("\nExample 4: Preserve environment variables in the resulting image")
prepared = await image.run(
shell="true",
env={"MY_PERSISTED_VAR": "persisted_value"},
preserve_env=True,
disposable=False,
)
result = await prepared.run("/bin/printenv", args=["MY_PERSISTED_VAR"])
print(f"Result: {result.stdout=}, {result.exit_code=}")
```
See [`ContreeImage.run()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImage.run) and [`ContreeSession.run()`](./reference/session#contree_sdk.sdk.objects.session.ContreeSession.run) for command execution details.
```python theme={null}
image = client.images.use("alpine:3.20", strict=True)
print(f"Pulled {image=}")
print("\nExample 1: Simple command execution")
result = image.run("/bin/echo", args=["Hello from command parameter!"]).wait()
print(f"Result: {result.stdout=}, {result.exit_code=}")
print("\nExample 2: Command with arguments")
result = image.run("/bin/ls", args=["-la", "/tmp"]).wait()
print(f"Result: {result.stdout=}, {result.exit_code=}")
print("\nExample 3: Command with environment variables")
result = image.run("/bin/printenv", args=["MY_VAR"], env={"MY_VAR": "test_value"}).wait()
print(f"Result: {result.stdout=}, {result.exit_code=}")
print("\nExample 4: Preserve environment variables in the resulting image")
prepared = image.run(
shell="true",
env={"MY_PERSISTED_VAR": "persisted_value"},
preserve_env=True,
disposable=False,
).wait()
result = prepared.run("/bin/printenv", args=["MY_PERSISTED_VAR"]).wait()
print(f"Result: {result.stdout=}, {result.exit_code=}")
```
See [`ContreeImageSync.run()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImageSync.run) and [`ContreeSessionSync.run()`](./reference/session#contree_sdk.sdk.objects.session.ContreeSessionSync.run) for command execution details.
### Command vs Shell Mode
* **Command mode**: Use `command="/bin/ls"` with `args=["-la", "/tmp"]` for direct execution without shell interpretation
* **Shell mode**: Use `shell="ls -la /tmp"` for shell commands with pipes, redirects, and wildcards
* **Environment variables**: Pass `env={"VAR": "value"}` to set environment for command execution
### Preserving Environment Variables
By default, values passed through `env` are available only to the current command. Set `preserve_env=True`
with `disposable=False` when those variables should be written into the resulting image and inherited by
later commands:
```python theme={null}
prepared = await image.run(
shell="true",
env={"MY_PERSISTED_VAR": "persisted_value"},
preserve_env=True,
disposable=False,
)
result = await prepared.run("/bin/printenv", args=["MY_PERSISTED_VAR"])
```
```python theme={null}
prepared = image.run(
shell="true",
env={"MY_PERSISTED_VAR": "persisted_value"},
preserve_env=True,
disposable=False,
).wait()
result = prepared.run("/bin/printenv", args=["MY_PERSISTED_VAR"]).wait()
```
On the ConTree side, `preserve_env=True` merges the image’s existing `metadata/env` entries with the `env`
values from the request, with request values taking priority, then writes the merged values back to
`metadata/env` in the resulting image. Setting a variable to an empty string removes it from the preserved
environment.
## Working with Files
You can upload and use files in your commands by specifying local file paths or pre-uploaded file objects:
```python theme={null}
image = await client.images.use("busybox:latest")
print(f"Using {image=}")
print("\nExample 1: Local file upload to image")
with NamedTemporaryFile(mode="w", suffix=".txt") as test_file:
test_file.write("some txt file\nsecond line\n\nlast line\n")
test_file.flush()
result = await image.run(shell=f"cat /{test_file.name.split('/')[-1]} | grep line", files=[test_file.name])
print(f"Run with local file: {result.stdout=}, {result.exit_code=}")
print("\nExample 2: Upload file via contree.files and use in image")
with NamedTemporaryFile(mode="w", suffix=".sh") as script_file:
script_file.write("#!/bin/sh\necho 'Hello from uploaded script'\necho 'Working directory:'\npwd\n")
script_file.flush()
uploaded_file = await client.files.upload(script_file.name)
print(f"Uploaded file: {uploaded_file=}")
result = await image.run(shell="sh /file.sh", files={"file.sh": uploaded_file})
print(f"Run with uploaded file: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
print("\nExample 3: Bake files into a new image with apply_files")
with NamedTemporaryFile(mode="w", suffix=".txt") as f:
f.write("hello from baked file\n")
f.flush()
baked = await image.apply_files({"baked.txt": f.name})
result = await baked.run(shell="cat /baked.txt")
print(f"File is present in new image: {result.stdout=}")
print("\nExample 4: Multiple files working together")
with (
NamedTemporaryFile(mode="w", suffix=".txt") as data_file,
NamedTemporaryFile(mode="w", suffix=".sh") as script_file,
):
data_file.write("apple\nbanana\ncherry\ndate\n")
data_file.flush()
script_file.write(
"#!/bin/bash\necho 'Processing data:'\ncat /data.txt | grep -E '^[ab]'"
"\necho 'Found items starting with a or b'"
)
script_file.flush()
result = await image.run(
shell="chmod +x /script.sh && sh /script.sh",
files={"data.txt": data_file.name, "script.sh": script_file.name},
)
print(f"Multiple files result: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
```
```python theme={null}
image = client.images.use("busybox:latest")
print(f"Using {image=}")
print("\nExample 1: Local file upload to image")
with NamedTemporaryFile(mode="w", suffix=".txt") as test_file:
test_file.write("some txt file\nsecond line\n\nlast line\n")
test_file.flush()
result = image.run(shell=f"cat /{test_file.name.split('/')[-1]} | grep line", files=[test_file.name]).wait()
print(f"Run with local file: {result.stdout=}, {result.exit_code=}")
print("\nExample 2: Upload file via contree.files and use in image")
with NamedTemporaryFile(mode="w", suffix=".sh") as script_file:
script_file.write("#!/bin/sh\necho 'Hello from uploaded script'\necho 'Working directory:'\npwd\n")
script_file.flush()
uploaded_file = client.files.upload(script_file.name)
print(f"Uploaded file: {uploaded_file=}")
result = image.run(shell="sh /file.sh", files={"file.sh": uploaded_file}).wait()
print(f"Run with uploaded file: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
print("\nExample 3: Bake files into a new image with apply_files")
with NamedTemporaryFile(mode="w", suffix=".txt") as f:
f.write("hello from baked file\n")
f.flush()
baked = image.apply_files({"baked.txt": f.name})
result = baked.run(shell="cat /baked.txt").wait()
print(f"File is present in new image: {result.stdout=}")
print("\nExample 4: Multiple files working together")
with (
NamedTemporaryFile(mode="w", suffix=".txt") as data_file,
NamedTemporaryFile(mode="w", suffix=".sh") as script_file,
):
data_file.write("apple\nbanana\ncherry\ndate\n")
data_file.flush()
script_file.write(
"#!/bin/bash\necho 'Processing data:'\ncat /data.txt | grep -E '^[ab]'"
"\necho 'Found items starting with a or b'"
)
script_file.flush()
result = image.run(
shell="chmod +x /script.sh && sh /script.sh",
files={"data.txt": data_file.name, "script.sh": script_file.name},
).wait()
print(f"Multiple files result: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
```
### File Upload Methods
You can provide files to commands in several ways:
* **Local file paths**: `files=["/path/to/local/file.txt"]` - Upload files directly
* **File mapping**: `files={"dest.txt": "/local/source.txt"}` - Upload with custom names
* **Pre-uploaded files**: `files={"script.sh": uploaded_file_object}` - Use files uploaded via `client.files.upload()`
## Advanced I/O Handling
You can use Python I/O objects for more sophisticated input/output handling:
```python theme={null}
image = await client.images.use("busybox:latest")
print(f"Using {image=}")
print("\nExample 1: StringIO for stdin and stdout")
stdin_io = StringIO("apple\nbanana\ncherry\ndate\n")
stdout_io = StringIO()
result = await image.run(shell="grep 'a' | sort", stdin=stdin_io, stdout=stdout_io)
print(f"StringIO result: exit_code={result.exit_code}")
print(f"Output in StringIO: {stdout_io.getvalue()=}")
print(f"result.stdout is the StringIO object: {result.stdout is stdout_io}")
print("\nExample 2: PIPE for stderr capture")
result = await image.run(shell="echo 'to stdout'; echo 'to stderr' >&2; exit 0", stderr=PIPE)
print(f"PIPE stderr: {result.stdout=}")
print(f"Stderr content: {result.stderr.read().decode()=}")
print(f"Stderr type: {type(result.stderr).__name__}")
print("\nExample 3: Output to bytes")
result = await image.run(shell="echo 'Hello bytes world'", stdout=bytes)
print(f"Bytes output: {result.stdout=}")
print(f"Output type: {type(result.stdout).__name__}")
print("\nExample 4: open() file object for input")
with NamedTemporaryFile(mode="w", suffix=".txt") as temp_file:
temp_file.write("line1\nline2\nline3\n")
temp_file.flush()
with open(temp_file.name) as file_obj:
result = await image.run(shell="wc -l", stdin=file_obj)
print(f"File object input: {result.stdout=}, {result.exit_code=}")
print("\nExample 5: BytesIO for binary data")
binary_data = BytesIO(b"binary\ndata\nlines\n")
result = await image.run(shell="wc -l", stdin=binary_data)
print(f"BytesIO input: {result.stdout=}, {result.exit_code=}")
```
See [`run()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImage.run) for I/O parameter details.
```python theme={null}
image = client.images.use("busybox:latest")
print(f"Using {image=}")
print("\nExample 1: StringIO for stdin and stdout")
stdin_io = StringIO("apple\nbanana\ncherry\ndate\n")
stdout_io = StringIO()
result = image.run(shell="grep 'a' | sort", stdin=stdin_io, stdout=stdout_io).wait()
print(f"StringIO result: exit_code={result.exit_code}")
print(f"Output in StringIO: {stdout_io.getvalue()=}")
print(f"result.stdout is the StringIO object: {result.stdout is stdout_io}")
print("\nExample 2: PIPE for stderr capture")
result = image.run(shell="echo 'to stdout'; echo 'to stderr' >&2; exit 0", stderr=PIPE).wait()
print(f"PIPE stderr: {result.stdout=}")
print(f"Stderr content: {result.stderr.read().decode()=}")
print(f"Stderr type: {type(result.stderr).__name__}")
print("\nExample 3: Output to bytes")
result = image.run(shell="echo 'Hello bytes world'", stdout=bytes).wait()
print(f"Bytes output: {result.stdout=}")
print(f"Output type: {type(result.stdout).__name__}")
print("\nExample 4: open() file object for input")
with NamedTemporaryFile(mode="w", suffix=".txt") as temp_file:
temp_file.write("line1\nline2\nline3\n")
temp_file.flush()
with open(temp_file.name) as file_obj:
result = image.run(shell="wc -l", stdin=file_obj).wait()
print(f"File object input: {result.stdout=}, {result.exit_code=}")
print("\nExample 5: BytesIO for binary data")
binary_data = BytesIO(b"binary\ndata\nlines\n")
result = image.run(shell="wc -l", stdin=binary_data).wait()
print(f"BytesIO input: {result.stdout=}, {result.exit_code=}")
```
See [`run()`](./reference/image#contree_sdk.sdk.objects.image.ContreeImageSync.run) for I/O parameter details.
### Supported I/O Types
* **StringIO**: For text-based input/output
* **BytesIO**: For binary data handling
* **File objects**: Use `open()` file handles directly
* **PIPE**: Capture stderr/stdout as byte streams
* **bytes type**: Get output as bytes instead of strings
## Subprocess-like Interface (Sync Only)
You can use a subprocess-like interface for more control over process execution:
```python theme={null}
image = client.images.use("busybox:latest")
print(f"Using {image=}")
print("\nExample 1: Basic popen with wait()")
process = image.popen(["/bin/ls", "-la"], cwd="/bin")
process.wait()
print(f"Process completed: returncode={process.returncode}")
print(f"Output: {process.stdout[:100]}...")
print("\nExample 2: Shell command with stdout and stderr")
process = image.popen("echo 'Hello stdout' && echo 'Hello stderr' >&2", shell=True)
process.wait()
print(f"Stdout: {process.stdout=}")
print(f"Stderr: {process.stderr=}")
print("\nExample 3: Using communicate() for input/output")
process = image.popen(["/bin/grep", "apple"])
stdout, stderr = process.communicate(input="apple\nbanana\ncherry\napple pie\n")
print(f"Grep results: {stdout=}")
print(f"Return code: {process.returncode=}")
print("\nExample 4: Environment variables")
process = image.popen(
"echo $MY_VAR && echo $ANOTHER_VAR", shell=True, env={"MY_VAR": "hello_world", "ANOTHER_VAR": "test_value"}
)
process.wait()
print(f"Environment output: {process.stdout=}")
print("\nExample 5: Error handling")
process = image.popen(["/bin/ls", "/nonexistent"])
process.wait()
print(f"Error case: returncode={process.returncode}")
print(f"Error output: {process.stderr=}")
```
See [`ContreeProcessSync`](./reference/subprocess#contree_sdk.sdk.objects.subprocess.ContreeProcessSync) for the full subprocess API.
### Popen Features
* **Process control**: Use `wait()`, `communicate()`, and check `returncode`
* **Environment variables**: Pass custom `env` dictionary
* **Working directory**: Set `cwd` parameter
* **Shell commands**: Enable with `shell=True`
* **Error handling**: Check `returncode` and `stderr` for failures
## Command Parameters
### Core Parameters
* **`shell`**: Execute as shell command (e.g., `"ls -la | grep txt"`)
* **`command`**: Executable path (e.g., `"/bin/ls"`)
* **`args`**: Command arguments as tuple (e.g., `("-la", "/tmp")`)
* **`stdin`**: Input data (string, bytes, or I/O object)
* **`env`**: Environment variables as dictionary
### I/O Parameters
* **`stdout`**: Redirect stdout (StringIO, BytesIO, file path, or `bytes`)
* **`stderr`**: Redirect stderr (StringIO, BytesIO, PIPE, or `bytes`)
* **`files`**: Upload files (list of paths or dict mapping)
### Execution Parameters
* **`cwd`**: Working directory inside container
* **`disposable`**: Whether to persist changes (default: True for runs, False for sessions)
* **`preserve_env`**: Whether to persist `env` values into the resulting image environment
* **`tag`**: Tag to assign to the resulting image after execution (e.g. `tag="myapp:v2"`)
## Result Objects
Command execution returns result objects with:
* **`stdout`**: Command output as string (or specified type)
* **`stderr`**: Error output as string (or specified type)
* **`exit_code`**: Process exit code (0 = success)
* **`uuid`**: UUID of the resulting image state
# Sandboxes for SWE agents
Source: https://docs.tokenfactory.nebius.com/sandboxes/swe-agents
Researching SWE agents is hard: there are thousands of heavy-weight environments, setup is slow, and reproducibility is fragile. Sandboxes provides a branchable, VM-isolated sandbox designed for rapid experimentation across thousands of SWE environments — over 7,000 are preloaded out of the box.
## Purpose-built for SWE agent research
* Thousands of ready-to-run SWE environments, so you can benchmark agents with minimal infrastructure setup.
* Git-like branching to try multiple patches or strategies in parallel from the same checkpoint, then keep only the winning branch: MCTS, beam search, rollbacks, value-function estimation.
* VM-level isolation with per-run metrics to keep generated code contained and experiments auditable.
## Environment catalog
Pick from the preloaded catalog rather than building or pulling terabytes of images yourself. What's included now:
* [SWE-bench Verified](https://huggingface.co/datasets/princeton-nlp/SWE-bench_Verified)
* [SWE-rebench](https://huggingface.co/datasets/nebius/SWE-rebench/viewer/default/filtered)
* [SWE-rebench-V2](https://huggingface.co/datasets/nebius/SWE-rebench-V2)
## Integrations
Terminal-first agent workflows, scripted runs, session branching, and filesystem inspection.
Integrate your own agent via contree-sdk.
Plug sandboxes into AI assistants that speak the Model Context Protocol.
# Switch to Token Factory
Source: https://docs.tokenfactory.nebius.com/switch
Nebius Token Factory provides an OpenAI-compatible API, making it easy to migrate existing OpenAI integrations with minimal changes.
## Switching from OpenAI
Here is a standard OpenAI example:
```python theme={null}
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-5.2",
input="Write a one-sentence bedtime story about a unicorn."
)
print(response.output_text)
```
To switch to Nebius Token Factory:
1. **Set the `base_url`** to the Token Factory endpoint:
`https://api.tokenfactory.nebius.com/v1/`
2. **Set your API key** using the `NEBIUS_API_KEY` environment variable. Get your key at: [https://tokenfactory.nebius.com/](https://tokenfactory.nebius.com/)
3. **Specify the model** you want to use (for example, `moonshotai/Kimi-K2.5`).
That's it - no other changes are required.
## Full Example Using Token Factory
```python lines theme={null}
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.tokenfactory.nebius.com/v1/",
api_key=os.environ.get("NEBIUS_API_KEY")
)
response = client.chat.completions.create(
model="moonshotai/Kimi-K2.5",
messages=[
{
"role": "system",
"content": "You are a helpful assistant"
},
{
"role": "user",
"content": [
{
"type": "text",
"text": "Write a haiku about cats"
}
]
}
]
)
print(response.to_json())
```
# Groups & Access management
Source: https://docs.tokenfactory.nebius.com/team-access/groups
Learn about access management on different levels
Being listed as a user in an organization does **not** automatically grant access to its resources.\
Each user or service account must be assigned to a **group** that defines their access level within the organization or its projects.
Groups exist at two levels:
* **Organization-level Groups** — control access to organization-wide settings and projects.
* **Project-level Groups** — control access to resources within individual projects.
### **Organization-level Groups**
Organization-level groups define permissions for managing the overall workspace, its billing, and projects.
| **Group** | **Description** |
| :------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Admin** | Full access to organization settings, billing, and all projects. Can invite external users, manage organization projects, and configure access rights. |
| **Billing Manager** | Can manage billing settings and view usage statistics across the organization. Cannot access project resources or invite users. |
| **No Group** | Users added without a role at the organization level. They can later be granted access to specific project-level resources. |
#### **Organization-level Permissions**
| **Action** | **Admin** | **Billing Manager** |
| :------------------------------- | :--------------------------- | :--------------------------- |
| **Usage & Consumption** | View | View |
| **Projects and Resources** | View, Create, Update, Delete | No Access |
| **Billing Settings** | View, Create, Update, Delete | View, Create, Update, Delete |
| **Payments** | View | View |
| **Organization Access (Users)** | View, Add, Remove | No Access |
| **Zero Data Retention Settings** | View, Change | No Access |
**Note:** Organization Admins automatically have administrative privileges across all projects in the organization.
### **Project-level Groups**
Project-level groups control access to specific project resources and operations.
| **Group** | **Description** |
| :--------- | :----------------------------------------------------------------------------------------------------------- |
| **Admin** | Full control over project resources. Can invite organization members to the project and manage their access. |
| **Member** | Can create and manage project resources but cannot invite new users or change access permissions. |
#### **Project-level Permissions**
| **Action** | **Admin** | **Member** |
| :-------------------------- | :---------------- | :-------------------------- |
| **Project Access (Users)** | View, Add, Remove | No Access |
| **API Keys** | Create, Delete | Create, Delete own API keys |
| **Files API** | Full Access | Full Access |
| **Fine-tuning API** | Full Access | Full Access |
| **Batch API** | Full Access | Full Access |
| **Dedicated Endpoints API** | Full Access | Full Access |
| **Prompt Presets** | Full Access | Full Access |
| **Public Endpoints API** | Full Access | Full Access |
### **Inviting Users to Groups**
To grant access to organization or project resources:
1. Invite a user to the organization.
2. Assign the user to the appropriate **Organization Group** or **Project Group**.
For detailed instructions, see the [**User Invitations**](https://docs.tokenfactory.nebius.com/team-access/invitations) section.
# User Invitations
Source: https://docs.tokenfactory.nebius.com/team-access/invitations
Learn how to invite new users to Organization and add users to Projects
## **Inviting Users to an Organization**
Users with **Organization Admin** rights can invite new users to join the organization.\
Invitations are sent by email and remain valid for **three days**. Once accepted, the user can sign in using any available authentication method.
### **To invite a user through the Web UI**
1. In the top header, open the **Organization** dropdown and click the **cog** **icon**.
* Alternatively, click your **Profile** icon and select **Organization Settings** from the menu.
2. On the **Organization** page, open the **Team & Access** tab.
3. Click **Invite**.
4. In the dialog window, enter the **email address** of the user you want to invite.
5. (Optional) Select an **Organization Group** to assign the appropriate access level.
* You can change the user’s group at any time.
* For more details, see [**Organization Groups**](https://docs.tokenfactory.nebius.com/team-access/groups#organization-level-groups).
6. Click **Invite**.
An invitation email will be sent to the specified address.\
Once the user accepts, they can sign in and access the organization based on their assigned group.
## **Inviting Users to a Project**
Users with **Project Admin** rights can add existing members of the organization to a specific project.\
If you also have **Organization Admin** rights, you can invite new users directly to both the organization and the project.
### **To invite a user through the Web UI**
1. In the top header, open the **Project** dropdown and click the **cog** **icon**.
* Alternatively, click your **Profile** icon and select **Project Settings** from the menu.
2. On the **Project** page, open the **Team & Access** tab.
3. Click **Invite**.
4. In the dialog window, choose one of the following options:
* **Invite new users** (requires Organization Admin rights): specify the user’s email to invite them to both the organization and the project.
* **Add existing users**: select users who are already part of the organization and grant them project access.
5. (Optional) Assign the user to a **Project Group** to define their permissions.
* You can change the group assignment at any time.
* For more details, see [**Project Groups**](https://docs.tokenfactory.nebius.com/team-access/groups#project-level-groups).
6. Click **Invite**.
The user will immediately gain access to the project once added or once their invitation is accepted.
# Organizations and Projects
Source: https://docs.tokenfactory.nebius.com/team-access/org-projects
Learn how to work with Organizations and Projects in Nebius Token Factory
### **Hierarchy Overview**
The **Nebius Token Factory resource model** follows a simple hierarchical structure:
**Organization → Projects → Resources**
* At the top level, the **Organization** defines your overall workspace and user management.
* Each **Project** within an organization acts as an isolated environment for related resources and teams.
* **Resources**—such as models, endpoints, files, and datasets—exist inside a project and inherit its access settings.
```mermaid theme={null}
%%{init: {"flowchart": {"nodeSpacing": 25, "rankSpacing": 40}}}%%
flowchart LR
classDef rounded stroke:#4C37DC,fill:#1E1E1E,color:#fff,rx:15,ry:15;
Org["Organization"]
Org --> A["Project A"]
Org --> B["Project B"]
A --> A1["API Keys"]
A --> A2["Fine-tuning Jobs"]
A --> A3["Dedicated Endpoints"]
A --> A4["Files & Datasets"]
B --> B1["API Keys"]
B --> B2["Fine-tuning Jobs"]
B --> B3["Datasets"]
class Org,A,B,A1,A2,A3,A4,B1,B2,B3 rounded;
```
***
### **Organization**
An **organization** represents your primary workspace in Nebius Token Factory. It serves as a container for all your projects, access settings, and user accounts.
An organization includes:
* **Projects** that contain your compute, storage, and AI resources
* **Organization Groups** that define identity and access configurations
* **User accounts** with assigned groups and permissions
When you sign up for Nebius Token Factory, a personal organization is created automatically.\
You can also:
* Be invited to other organizations and collaborate on their projects
* Create additional organizations for separate teams or business units
To grant a user access to organizational resources, add them to the relevant **Organization Group**.
***
### **Projects**
A **project** is a logical container for your Nebius Token Factory resources. Projects help you organize assets and manage permissions within an organization.
Within a project, you can create and manage:
* **API keys**
* **Fine-tuning jobs**
* **Dedicated endpoints**
* **Files and datasets**
Each project includes **Project Groups** that control access to project resources.\
Projects can be **multi-region**, allowing you to group resources by product, team, use case, or any other organizational criteria.
When you first sign up for Nebius Token Factory, a **default project** is created automatically for your resources.
# Overview
Source: https://docs.tokenfactory.nebius.com/team-access/overview
Collaborate securely with Team Management & Role-Based Access
**Teams & Access Management** enables centralized control over user access to your resources. It ensures that only authorized users and accounts with assigned permissions can interact with your organization’s assets.
**Key Capabilities**
* Manage billing details and usage across your organisation
* Add team members to your organization or projects
* Define access levels using predefined access groups
* Split your workspace into distinct projects and invite only relevant collaborators
* Manage permissions independently for each project and the overall organization
This functionality ensures **secure, compliant, and organized collaboration** across all workspaces and is available to all users, regardless of service tier.
# Configure Single Sign-On
Source: https://docs.tokenfactory.nebius.com/team-access/sso
Learn how to configure single sign-on
A federation could be configured for the Nebius Token Factory to allow your organization's users login using a SAML 2.0 compatible single sign-on provider.
SSO is configured in two systems:
* Nebius Token Factory, where you create and configure a federation.
* Identity Provider: create an application and connect the application to the federation.
After the federation and application are set up, users can sign in to Nebius Token Factory.
Following 4 steps will guide you through the process.
## 1. Create an application in the Identity Provider
Go to Admin Console → Applications → Application and choose "Create App Integration".
Choose "SAML 2.0" sign-in method and set the following parameters on the next screen:
* **Single sign-on URL**: [https://auth.tokenfactory.nebius.com/login/saml2/provider/federation-id](https://auth.tokenfactory.nebius.com/login/saml2/provider/federation-id)
* **Audience URI (SP Entity ID)**: [https://auth.tokenfactory.nebius.com/saml2/rp/federation-id](https://auth.tokenfactory.nebius.com/saml2/rp/federation-id)
* **Name ID format**: Unspecified
* **Application username**: Okta username
Continue and tick the option "This is an internal app that we have created" on the next screen.
**Note**
`federation-id` is used temporarily until you create a federation and get its ID. After that, replace `federation-id` with an actual one.
## 2. Create a federation in Nebius Token Factory
1. Install the Nebius CLI tool (an installation script works for macOS and Linux):
```
curl -sSL https://storage.eu-north1.nebius.cloud/cli/install.sh | bash
```
2. Create a configuration profile:
```
nebius profile create \
--profile \
--endpoint api.nebius.cloud \
--federation-endpoint auth.tokenfactory.nebius.com \
--parent-id
```
An organization ID starts with `aitenant-` and could be found on [the organization settings page](https://tokenfactory.nebius.com/organization/projects).
3. Run the following command:
```
nebius iam federation create \
--parent-id \
--name \
--user-account-auto-creation=true \
--active=true \
--saml-settings-sso-url \
--saml-settings-idp-issuer
```
4. Copy and save the federation ID. It is returned in the `metadata.id` field of the command output.
## 3. Change the SAML settings of the application
1. Replace `federation-id` with an actual federation ID in the Okta application configuration.
2. Navigate to the application configuration page on the Admin Console.
3. Then scroll down to the "SAML Settings" section and replace `federation-id` with the created federation ID in the following fields:
* Single Sign On URL: `https://auth.tokenfactory.nebius.com/login/saml2/provider/`
* Recipient URL: `https://auth.tokenfactory.nebius.com/login/saml2/provider/`
* Destination URL: `https://auth.tokenfactory.nebius.com/login/saml2/provider/`
* Audience Restriction: `https://auth.tokenfactory.nebius.com/saml2/rp/`
## 4. Add a signing certificate to the federation
Download a certificate from the Identity Provider.
1. Navigate to the application configuration page on the Admin Console.
2. Switch to the "Sign On" tab and scroll down to the "SAML Signing Certificates" section.
3. Push "Generate new certificate" and then "Actions → Download certificate" on a line with freshly created active certificate.
Then, add the certificate to the federation:
1. Prepare the `certificate.json` file:
```
{
"metadata": {
"parent_id": ""
},
"spec": {
"description": "certificate for a federation",
"data": "-----BEGIN CERTIFICATE-----\n\n-----END CERTIFICATE-----\n"
}
}
```
Specify the certificate body from the downloaded file and the federation ID.
In this file, the certificate body is split into several lines. Paste it as a single line to `federation-cert.json`.
2. Apply the certificate file:
```
nebius iam federation-certificate create --file federation-cert.json
```
## Log in to Nebius Token Factory using the configured SSO
1. Open the Nebius Token Factory web console.
2. Click the Get started with SSO button.
3. Enter the federation ID and click the Sign in button.
A successful login means that you have correctly configured the federation and SSO.
## Assigning new users to the groups
SSO users are not included in any Access groups on first login. An organization administrator should assign them to an Access group to provide the required permissions on the platform.
# Prompt presets
Source: https://docs.tokenfactory.nebius.com/utilities/prompt-presets
Learn how to save your prompts and reuse them
[**Prompt presets**](https://tokenfactory.nebius.com/prompt-presets) let you save and reuse model setups you’ve tested in Nebius Token Factory, making it easy to manage and share configurations.
In the **Inference playground**, you can store the model parameters, system prompt, and few-shot examples as a prompt preset. Saved presets appear on the [**Prompt presets**](https://tokenfactory.nebius.com/prompt-presets) page, where you can:
* Reopen them in the playground for further testing
* Export them as code for integration into your application
* Share them with your team
### Prompt preset contents
A prompt preset is based on a single model setup from the playground and includes:
* **Text-to-text model** — Other model types are not supported.
* **Model parameters** available in the playground (e.g., temperature, max tokens). Parameters available only via API are not included.
* **System prompt** (if added in your setup).
* **Few-shot examples** — user prompts and AI responses you’ve added.
Your actual chat history with the model is not saved in the preset.
### Creating a preset
1. In the left panel, click **Save preset**.
If using Compare mode, click → **Save preset** in the panel of the desired model.
2. In the dialog, enter a name and (optional) tags.
3. Click **Save preset**.
### Using presets
You can manage all saved presets on the [**Prompt presets**](https://tokenfactory.nebius.com/prompt-presets) page.
1. **Open in playground** — Click a preset to load it back into the playground.
2. **View as code** — Click → **View code** to generate code for use in your app.
3. **Share** — Click → **Share** to copy a URL.
Recipients must have a Nebius Token Factory account to access presets and other inference features.