rath.backend#
Backend abstractions, sandbox handles, backend tool payloads, execution results, registry, and stream.
Source#
Module |
Source |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Public contract#
Backend interface#
API |
Returns |
Description |
|---|---|---|
|
|
Static availability check. |
|
|
Backend class-level capabilities. |
|
|
Supported payload types. |
|
|
Opens a sandbox handle with |
|
|
Closes and releases resources. |
|
|
Executes the payload. |
Sandbox handle lifecycle#
Each Session.sandbox slot owns one sandbox reference; the backend closes the
handle only after the final reference is released.#
API |
Behavior |
|---|---|
|
Read-only live reference count. |
|
Adds one reference; raises |
|
Drops one reference; closes through the backend when the count reaches zero. |
|
Acquires on enter and releases on exit. |
|
Runs a backend payload unless the sandbox is closed. |
|
Creates a FIFO worker stream bound to this sandbox. |
Every Session.sandbox slot owns one reference. Session.bind_sandbox(...), Session.require_sandbox(), run_session_loop(...), run_session_compress(...), fork(), and detach() all preserve this ownership rule. Refcount acquire/release is lock-protected in v1.2, and the backend close happens outside the lock to avoid double-close races.
Sandbox spec#
Field |
Type |
Description |
|---|---|---|
|
|
Image name that the backend may use. |
|
|
Entrypoint that the backend may use. |
|
|
Sandbox environment variables. |
|
|
Sandbox lifetime or creation-timeout semantics. |
|
|
Local working directory or OpenSandbox host bind source. |
Backend tool payloads#
Payload |
Fields |
Returns |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Registry#
Function |
Behavior |
|---|---|
|
Decorator that registers a backend class. |
|
Returns registered backend names. |
|
Returns the per-process singleton backend instance. |
|
Returns the backend class. |
|
Returns true when the backend is registered and class availability is true. |
|
Returns the first available backend instance. |
|
Sets and gets the default backend. |
Exceptions#
Exception |
Trigger |
|---|---|
|
|
|
|
|
The backend implementation does not support a payload type. |
|
The opensandbox backend is opened directly when the OpenSandbox SDK is missing. |
Built-in backends#
Backend |
Behavior |
|---|---|
|
Host-side subprocess plus filesystem workspace. Automatically registered after importing |
|
Optional SDK backend. The container root is |
OpenSandbox uses opensandbox/code-interpreter:v1.0.2 by default. If a requested host bind is rejected by the server allowlist, OpenRath retries once with an empty workspace unless RATH_OPENSANDBOX_STRICT_WORKSPACE_BIND=1 is set.
Persistent sandbox identities#
PersistentSandboxRegistry stores reusable sandbox identities under .openrath/sandboxes/; it does not hold live BackendSandbox handles.
Persistent sandbox records make local working directories and OpenSandbox remote ids discoverable across process boundaries without keeping live handles open.#
Backend |
Storage |
|---|---|
local |
|
opensandbox |
|
API |
Behavior |
|---|---|
|
Creates a fresh local sandbox directory. |
|
Creates or reuses a local sandbox directory. |
|
Local sandbox cleanup helpers. |
|
Stores a remote sandbox identity. |
|
Remote record lifecycle helpers. |
Autodoc#
- class rath.backend.Backend[source]#
Abstract base class for sandbox backends.
Subclasses must:
Set the
nameclass attribute and register viarath.backend.register().Implement the static
is_available,capabilitiesandsupported_callsclassmethods.Implement the instance method
sandbox_count.Implement the async hooks
_aopen,_acloseand_adispatch. The syncopen/close/dispatchdefaults below route these throughrath._async.runtime.OpenRathRuntimeso a single background loop services every subsystem concurrently.
- abstractmethod classmethod is_available() bool[source]#
Return whether this backend is usable in the current environment.
Must be cheap (microseconds, no network, no subprocess). Examples: check that a required SDK is importable, or that a config file or environment variable is present.
- abstractmethod classmethod capabilities() Capabilities[source]#
Return the static capability description of this backend type.
- abstractmethod classmethod supported_calls() frozenset[type[BackendTool]][source]#
Return
BackendToolsubclasses this backend handles.
- abstractmethod sandbox_count() int[source]#
Return the number of open sandboxes managed by this instance.
- open(spec: BackendSandboxSpec | None = None) BackendSandbox[source]#
Open a fresh sandbox and return its handle (sync facade).
- close(sandbox: BackendSandbox) None[source]#
Close
sandboxand release resources (sync facade).Calling close on an already-closed sandbox is a no-op.
- dispatch(sandbox: BackendSandbox, call: BackendTool) ToolResult | bool[source]#
Execute
callagainstsandboxand return its result (sync facade).
- class rath.backend.BackendSandbox(backend: Backend, handle: str, spec: BackendSandboxSpec | None = None, closed: bool = False, _refcount: int = 0, _refcount_lock: allocate_lock = <factory>)[source]#
Sandbox handle with reference counting.
Lifecycle is governed by
_refcount: eachSession.sandboxslot, eachwith sandbox:block, and any explicitacquire()counts as one reference.release()decrements and, when the count reaches zero, callsbackend.close(self). There is no “force close” path — callers that want immediate teardown must drop all references.Backend.open()returns a sandbox with_refcount == 0. The caller is expected to either bind it to aSession(which acquires) or enterwith sandbox:(which acquires) before it can be safely held.- acquire() BackendSandbox[source]#
Add one reference; return
selffor chaining.
- class rath.backend.BackendSandboxSpec(image: str | None = None, entrypoint: Sequence[str] | None = None, env: Mapping[str, str] | None = None, timeout: timedelta | None = None, working_dir: str | None = None)[source]#
User-facing description of a sandbox to open.
Fields are intentionally optional. Each backend may ignore fields that do not apply (e.g.
LocalBackendignoresimage).
- class rath.backend.BackendToolCommandRun(cmd: str | Sequence[str], env: Mapping[str, str] | None = None, cwd: str | None = None, stdin: bytes | None = None, timeout: float | None = None)[source]#
Run a shell command inside the sandbox.
- class rath.backend.BackendToolFilesRead(path: str, encoding: str | None = 'utf-8')[source]#
Read a file from the sandbox.
- class rath.backend.BackendToolFilesWrite(path: str, data: bytes | str, mode: int = 420)[source]#
Write a file inside the sandbox with the given Unix mode.
- class rath.backend.BackendToolFilesList(path: str)[source]#
List entries (non-recursive) under a sandbox directory.
- class rath.backend.BackendToolFilesExists(path: str)[source]#
Check whether a path exists inside the sandbox.
- class rath.backend.BackendToolCodeRun(code: str, language: str = 'python', timeout: float | None = None)[source]#
Execute a code snippet inside the sandbox in the given language.
- class rath.backend.CommandResult(exit_code: int, stdout: bytes, stderr: bytes, elapsed_ms: float)[source]#
Result of
BackendToolCommandRun.
- class rath.backend.FileContent(data: bytes | str)[source]#
Result of
BackendToolFilesRead.dataisstrwhen the call was made with anencodingset, orbyteswhenencoding=None.
- class rath.backend.FileEntries(entries: tuple[FileEntry, ...])[source]#
Result of
BackendToolFilesList.Entries are sorted by
namefor stable ordering.
- class rath.backend.FileWriteResult(bytes_written: int)[source]#
Result of
BackendToolFilesWrite.Holds the number of bytes actually written so that callers can verify the write without re-reading the file.
- class rath.backend.CodeResult(text: str | None, stdout: bytes, stderr: bytes, error: str | None)[source]#
Result of
BackendToolCodeRun.textholds the value of the last expression when the underlying runtime supports value extraction (e.g. a real code interpreter). For backends that only execute the script as a subprocess,textisNone.
- class rath.backend.ToolExecutionFailure(kind: str, message: str, detail: str | None = None)[source]#
Structured failure from
dispatch().Prefer returning this instead of raising when the tool invocation itself failed or is unsupported, so the session loop can surface text to the model.
- class rath.backend.Stream(sandbox: BackendSandbox, *, buffer: int = 0)[source]#
Per-sandbox FIFO queue of tool-call operations (blocking worker thread).
Thread-safety contract:
Once
__exit__returns, no caller can submit new ops; the worker has joined, every future the public methods returned is either done or will fail withRuntimeError("stream is closed")(no hanging).Public methods do their
_check_closedcheck and the matching_queue.putunder the same lock acquisition so a concurrent__exit__cannot enqueue the shutdown signal in between.If the worker hits a fatal exception,
_fail_remainingempties the queue and fails every future on it.BaseException(e.g.KeyboardInterrupt) is re-raised after that drain so the worker thread terminates with a visible traceback instead of vanishing.
- class rath.backend.persistence.PersistentSandboxRegistry[source]#
Filesystem-backed registry of persisted sandbox identities.
Instances are cheap; everything lives on disk under
.openrath/sandboxes/{local,opensandbox}/. The class does no locking; callers that share a registry across threads should serialize writes externally.- alloc_local_id() UUID[source]#
Generate a new UUID and create its working directory.
Returns the UUID. Use
local_path()to resolve it back to apathlib.Path. Repeated calls always return a fresh UUID; for “give me one if missing, else reuse”, callensure_local()with an explicit id.
- ensure_local(sandbox_id: UUID | str) Path[source]#
Create the working directory for
sandbox_idif missing; return it.Idempotent. Useful when a caller already knows the id (e.g. from a persisted session header) and wants to rebind the same workdir.
- local_path(sandbox_id: UUID | str) Path[source]#
Resolve a local sandbox id to its on-disk working directory.
Does not check existence — use
ensure_local()to create on demand. Returns the path even when the directory has been removed, so callers can decide whether to recreate.
- delete_local(sandbox_id: UUID | str) bool[source]#
Remove the on-disk working directory for a local sandbox.
Returns
Truewhen the directory existed and was removed,Falsewhen it was already absent. Idempotent.
- prune_local(*, older_than: timedelta) list[UUID][source]#
Remove local sandbox dirs whose mtime is older than
older_than.Returns the list of removed ids in deletion order. Useful for a weekly cron / startup sweep —
PersistentSandboxRegistry().prune_local(older_than=timedelta(days=30)).
- record_remote(backend: str, remote_id: str, spec: BackendSandboxSpec | None = None, *, sandbox_id: UUID | None = None) UUID[source]#
Persist
remote_id(e.g. an OpenSandboxnative.id) under a new UUID.backendis the backend name (typically"opensandbox").specis theBackendSandboxSpecused to create the remote sandbox; it round-trips through the same JSON projection as the session header. Returns the registry UUID (NOTremote_id) so callers can keep using a stable local handle.
- touch_remote(sandbox_id: UUID | str) None[source]#
Update
last_used_aton the remote sandbox index file.Silently no-ops when the record is missing — callers should rely on
load_remote()first if presence matters.
- load_remote(sandbox_id: UUID | str) RemoteSandboxRecord | None[source]#
Read one remote-sandbox index file. Returns
Nonewhen missing.
- list_remote() list[RemoteSandboxRecord][source]#
Enumerate every remote sandbox record. Unreadable files are skipped.
- delete_remote(sandbox_id: UUID | str) bool[source]#
Remove the index file for a recorded remote sandbox.
Returns
Truewhen the file existed and was removed;Falsewhen absent. Does not kill the remote container on the server — that’s the caller’s responsibility via the backend’sclose().
- prune_remote(*, older_than: timedelta) list[UUID][source]#
Remove index files whose
last_used_atis older thanolder_than.Returns the deleted ids in deletion order. The remote containers themselves are not touched.
- reattach_remote(sandbox_id: UUID | str) BackendSandbox[source]#
Reattach to a previously recorded remote sandbox by its registry id.
Looks up the index file, then delegates to the named backend’s
attachmethod (currently onlyOpenSandboxBackendprovides one). Updateslast_used_aton success.Raises
KeyErrorwhen the registry id is unknown, andAttributeErrorwhen the recorded backend has noattach— either path produces a clear error rather than silently creating a new container.
- class rath.backend.persistence.RemoteSandboxRecord(*, schema_version: int, id: UUID, backend: str, remote_id: str, spec: BackendSandboxSpec | None, created_at: datetime, last_used_at: datetime, path: Path)[source]#
One
.openrath/sandboxes/opensandbox/<uuid>.jsondecoded.
- rath.backend.get(name: str) Backend[source]#
Look up a backend by name and return the per-process singleton instance.
The same instance is returned for repeated calls with the same
name, so per-backend caches (e.g.OpenSandboxBackend._natives) and the sandbox refcount are coherent across all sessions in the process.