What each component does
A memory Store keeps history only for that store instance. A SQLite Store keeps
it across Python restarts. The filesystem images themselves remain on the ConTree
server. Deleting an upload-cache tree removes its reuse records without changing session history.
Sandbox history and upload reuse share a memory Store by default. The
import and build helpers use a persistent SQLite Store by default. See
Advanced: assemble a sandbox yourself for component construction and ownership.
Bring your own storage
SyncStore and AsyncStore are the public storage interfaces. The SDK includes
Memory and SQLite implementations. Subclass the matching interface to implement
Redis or PostgreSQL storage. One Store holds both history and reusable results.
Tree kinds separate their purposes.
Store contains typed trees of immutable nodes. Session owns the selected position
and local branch names. See Implement history storage
for atomic insertion, pagination, and retention requirements.
Pass your Store through store= to helpers, sessions, and builders. Use sync implementations with sync APIs and async implementations
with async APIs. The SDK uses these interfaces, so changing the storage backend
does not require changing its execution code. Your application owns and closes
the supplied components.
Share one client
For a workflow with several operations, initialize a client once. Pass it throughclient= as a dependency to the import helper, build helper, and sandbox factory.
This reuses the connection and gives your application one place to configure it.
Each operation leaves this client open; the outer context closes it.
Use the build-context directory from
Create a small build context.
These programs import the base under the alpine:latest ConTree tag, build that
context, and read its result. The Dockerfile’s FROM alpine:latest resolves the
imported image.
- Sync
- Async
Select the session class and components
Some async snippets use top-levelawait. Run them inside an async function or
a compatible notebook. Complete programs use asyncio.run(main()).
session_type accepts a class. Without it, the context selects LazySession or
AsyncLazySession. An explicitly selected class must match the context mode:
session_type=AsyncLazySession with a synchronous with raises TypeError
before creating a client. The same rule applies to client_type and supplied
store, file transfer, and client objects.
- Sync
- Async
SyncExecutor or AsyncExecutor. Lazy subclasses
receive an underlying ContreeSession or ContreeAsyncSession, plus
session_kwargs. Other classes receive the client and the standard Session
constructor arguments, plus session_kwargs. Subclass an existing session to
retain its execution and lifecycle rules. The factory never substitutes a
different class for an explicitly selected class.
Selecting ContreeSession or ContreeAsyncSession saves each command by default
and starts the next command in a separate VM. Use disposable=True to leave
the session image unchanged. See Save after every command.
Snapshots are lightweight; saving after every command is supported. A snapshot
stops the VM and can return the same image UUID if files have not changed.
Uploaded-file cache entries are separated by endpoint, project, and token. The
cache does not replace history storage or enable uploads to an already running
LazySession VM.
Ownership and extension
Each context object permits one entry, including a failed entry. Re-entry, nesting, and mixing sync/async entry on that object raiseRuntimeError; call the
factory again to get a new context.
The factory closes resources it creates. Supplied clients, storage, and
file transfers remain caller-owned and must be ready for use. A session’s context
exit runs before the owned client closes. Async cleanup is joined even when the
caller cancels again; a body exception takes priority over cleanup errors.
All three factories call session_maker(context_type, ...) with their respective
context class. SandboxSessionContext composes SyncSandboxSessionContext and
AsyncSandboxSessionContext. For a custom context, supply a class accepting
SandboxConfig to session_maker. See Sandbox factories for the shared
parameter signature and context contracts.
For complete sync and async examples of the component assembly behind this
factory, see Advanced: assemble a sandbox yourself.
Tag a saved result
Run a setup command, save its result, and give that image a reusable name. This example selectsContreeSession or ContreeAsyncSession, which saves each command
by default as an independent operation. The default lazy sandbox instead
saves at a snapshot boundary; see Work with a Session.
- Sync
- Async
image="tag:prepared-base" in another sandbox. The tag refers to the saved
filesystem; it does not copy session history, environment overrides, or defaults.
Assigning an existing tag moves it to the new image.
Image listing and tag removal are available through list_images() and
delete_image_tag() on the client. Closing a client or local store does not
delete server images. Deleting a server image can invalidate saved session history.
Assemble the builder yourself
The helpers create a client and one SQLite Store for layer history and reusable uploads. They scope the build session to the context path and connection, call the Docker builder, and close the resources they created. Use the builder directly to manage these dependencies and their lifetime yourself. The following example keeps a builder inside the client context and uses its default memory Store. For persistence, passstore=SyncSQLiteStore(...) or its
async equivalent and close it yourself. Helpers accept the same dependency as store=.
Use builder_type= and builder_kwargs= to extend a builder through a helper.
Rebuild a multi-stage image after changing inputs
This standalone example createsmulti-context in the current directory. Its
first stage builds an output file. The second stage copies only that output with
COPY --from. Use a base image with cat and a POSIX shell.
The builder downloads regular files as bytes and attaches them at their final
paths to the next operation. The file specification carries permissions and
ownership, including explicit --chown/--chmod overrides.
For directory sources, the builder exports tar and asks the live API to unpack
it directly into the destination with merge enabled. This preserves existing
entries that are absent from the archive. The VM runs sleep infinity during
transfer. Explicit directory overrides run as subprocesses after unpacking and
before the builder signals the main process to save the layer. These commands
change only entries supplied by the archive. Directory copies require
sleep infinity and, for overrides, chown or chmod; they do not require tar.
Keep the same builder, store, and build session to observe layer reuse. Changing
an ignored file leaves the build inputs unchanged. Changing message.txt
invalidates the source RUN and the stage copy that depends on its output.
- Sync
- Async
0755 and does not restore
ownership or timestamps. Explicit directory overrides apply after unpacking. File attachments retain
the source file mode and ownership unless COPY overrides them.
Observe a live build
Use thebuild-context files from Build a Docker image for these examples.
Pass on_event= to receive BuildEvent notifications while a build runs.
Output events arrive before RUN completes and carry raw bytes. Decode or format
them in your application. Each operation uses the same reader for output and its
final result.
event.step.id is unique within one build. event.step.index is the zero-based
parsed directive index, including ARG and metadata instructions. Synthetic steps
have index=None. A nested step has parent_id set to its enclosing step’s ID.
A final COPY/ADD flush appears as a synthetic RUN :; a stage flush appears as a
child of the next FROM. If a nested step fails, both it and its parent report the
same exception.
Events include elapsed step time, image IDs, and the operation UUID when available.
The terminal RUN event also contains its typed InstanceResult, including nonzero
exit codes. A cache hit emits no output and no new operation UUID.
- Sync
- Async
build(). The async builder
accepts both synchronous and awaitable callbacks. Keep callbacks short. The async
example offloads local output writes so they do not block the event loop. For a
live UI, a callback can also feed a bounded queue that another task consumes.
A callback error, including a closed output pipe, aborts the build and cancels its
active RUN operation. Async task cancellation and sync interruption follow the
same cleanup path. Error notifications cannot replace the original exception.
Callbacks do not roll back completed layers. File, parse, and final tag errors
raise directly; these do not create directive events outside a running step.
Customize a build
Passon_step= to receive a BuildStepEvent for each parsed directive. It includes
duration, cache-hit status, image IDs, and any error. Keep callbacks short; a callback
exception can stop the build. The async builder also accepts an awaitable callback.
Use parser=, session_factory=, or execute_directive() for custom behavior.
See Customize the SDK and Dockerfile builders.
Upload cache lifetime and scope
COPY and ADD keep reusable upload records in the same Store as layer history. Tree kinds separate these records. Namespaces and layer hashes include the endpoint, project, and credential scope, so shared storage does not reuse another server’s UUIDs. Local paths and source URLs are retained through the public file-source registry. See Reuse uploads and retain their sources for enumeration and invalidation. The default upload lifetime is 90 days. Cache hits and URL responses with status 304 do not extend it. Overridecreate_context() and set
context.upload_cache_ttl to choose a shorter lifetime for your server. Expiry
causes a new upload when the directive runs; it does not invalidate an existing
image layer. A 304 response without a live cached upload raises DockerBuildError.
Local COPY uses a temporary snapshot for its content hash and upload. Async COPY
limits concurrent snapshots and uploads through context.upload_concurrency
(default four). URL ADD streams into a temporary file before upload and closes the
response body, including conditional responses. These paths bound memory usage;
temporary disk space must hold the files currently being prepared.