Runtime Checkpoint Control
This document records the current control-plane model for runtime checkpoint and checkout.
Layering
The external API is runtime-oriented. Callers control a runtime object by using
runtime_id and checkpoint_id; they do not need to know which session or
backend instance is carrying that runtime.
Internally, the current v1 implementation uses this layering:
| Layer | Current role | External exposure |
|---|---|---|
| Runtime | Control-plane object used for checkpoint and checkout | Exposed as runtime_id and RuntimeRecord |
| Session | Gateway execution state, conversation identity, lifecycle state, handle state, and resolved agent runtime snapshot | Mostly hidden behind runtime APIs; v1 uses runtime_id == session_id |
| Backend | Operation substrate for file, exec, search, export, and provider snapshots | Internal; backend ids are not returned by RuntimeRecord |
RuntimeRecord is derived from SessionRecord:
pub struct RuntimeRecord {
pub runtime_id: String,
pub conversation_id: String,
pub sender_id: String,
pub status: SessionLifecycleStatus,
pub created_at_ms: u64,
pub updated_at_ms: u64,
}
In v1, runtime_id is the same value as SessionRecord.session_id. The
backend binding is kept inside SessionRecord.backend_instance and the
BackendManager session index, which maps a session/runtime id to a backend id.
This keeps backend placement and provider-native ids out of the public runtime
record.
Public Runtime API
The daemon exposes runtime checkpoint control through the protected HTTP API.
Bearer authentication applies when [http] auth is configured.
| Endpoint | Request | Response | Meaning |
|---|---|---|---|
POST /api/v1/runtimes/checkpoint |
RuntimeCheckpointRequest |
RuntimeCheckpointResult |
Capture the current idle runtime as a checkpoint |
POST /api/v1/runtimes/checkpoint/delete-snapshot |
RuntimeCheckpointSnapshotDeleteRequest |
RuntimeCheckpointSnapshotDeleteResult |
Delete the provider snapshot/template referenced by a checkpoint |
POST /api/v1/runtimes/checkout |
RuntimeCheckoutRequest |
RuntimeCheckoutResult |
Create a new runtime from an existing checkpoint |
POST /api/v1/runtimes/pause |
RuntimePauseRequest |
RuntimePauseResult |
Snapshot an idle runtime and release its live backend |
POST /api/v1/runtimes/resume |
RuntimeResumeRequest |
RuntimeResumeResult |
Restore a paused runtime with the same runtime id |
RuntimeCheckpointRequest contains runtime_id, optional name, and optional
metadata. The result returns a generated checkpoint_id, the public
RuntimeRecord, and the previous checkpoint head for the same runtime when one
exists.
RuntimeCheckoutRequest contains checkpoint_id, optional conversation_id,
optional sender_id, and optional metadata. Checkout always creates a new
runtime branch in v1. The caller cannot provide the child runtime id; the service
generates it from the source runtime id plus a UUID suffix.
RuntimeCheckpointSnapshotDeleteRequest contains checkpoint_id. The service
looks up the checkpoint's internal BackendCheckpointRef and deletes only the
provider snapshot recorded there; callers cannot provide an arbitrary provider
template id. The checkpoint record remains in the process-local store, but its
provider snapshot id is cleared after successful or already-deleted provider
cleanup.
RuntimePauseRequest contains runtime_id, optional name, and optional
metadata. Pause requires the runtime to be idle. For E2B-backed runtimes it
creates or reuses a provider snapshot/template, deletes the live sandbox through
the backend release path, clears the runtime's backend binding, and stores the
paused checkpoint in memory.
RuntimeResumeRequest contains runtime_id and optional metadata. Resume
requires the runtime to be paused; it creates a new backend from the paused
checkpoint and keeps the same runtime_id.
Runtime Checkpoint Shape
The in-memory checkpoint store records:
checkpoint_idruntime_idparent_checkpoint_id- a
SessionRecordsnapshot - an optional
BackendCheckpointRef - creation time, name, and metadata
The store is process-local in v1. Restarting the daemon loses runtime checkpoints unless a later persistence layer is added.
Checkpoint and checkout require the source runtime to be idle. A running or busy
session returns SessionBusy because the session snapshot and backend snapshot
must describe a stable point in time.
Backend Checkpoint Behavior
Backend checkpointing is internal to runtime checkpointing.
BackendManager::checkpoint_backend accepts either a backend_id or a
session_id. Runtime checkpoint uses the session/runtime id path so it can stay
above the backend layer.
For E2B backends, checkpoint creates a provider snapshot by calling the E2B
snapshot API and stores the returned provider snapshot id inside
BackendCheckpointRef.provider_snapshot_id. Checkout uses that snapshot id to
create a new E2B-backed backend for the child runtime.
Providers without snapshot checkout support return UnsupportedBackend for
checkout. The current v1 implementation does not implement local restore.
E2B Sandbox Timing
E2B runtime checkpoint and checkout include provider-side sandbox work:
| Operation | Provider work | Notes |
|---|---|---|
POST /api/v1/runtimes/checkpoint |
Calls the E2B snapshot API for the source sandbox when the backend is dirty or has no reusable checkpoint | Clean backends with an existing checkpoint can reuse the prior BackendCheckpointRef and avoid a new provider snapshot |
POST /api/v1/runtimes/checkout |
Starts a new E2B sandbox from the provider snapshot and binds it to the child runtime id | The child runtime receives a generated id; callers cannot provide it in v1 |
POST /api/v1/runtimes/checkpoint/delete-snapshot |
Deletes the E2B snapshot/template by calling the E2B delete-template API | This is explicit cleanup for snapshots the caller no longer needs for future checkout |
POST /api/v1/runtimes/close |
Releases the runtime backend and deletes the E2B sandbox when no runtimes remain bound to that backend | Close time is separate from checkpoint/checkout timing |
POST /api/v1/runtimes/pause |
Calls the E2B snapshot API, then deletes the live sandbox | The checkpoint template remains available for resume |
POST /api/v1/runtimes/resume |
Starts a new E2B sandbox from the paused checkpoint template | Resume is subject to the active E2B sandbox limit |
Use curl -w '%{time_total}' around the checkpoint and checkout requests when
measuring from a client. That value is end-to-end HTTP latency as observed by the
caller; it includes daemon bookkeeping and E2B provider calls, but not the LLM
turns needed to prepare or validate the runtime state.
One local smoke run against an E2B backend on 2026-06-13 measured:
| Operation | Measured time_total |
|---|---|
| Runtime checkpoint | 1.516061s |
| Runtime checkout | 2.335461s |
These numbers are operational examples rather than SLA values. They can vary with E2B service latency, network path, snapshot size, template cold/warm state, and daemon host load.
Dirty Tracking
Backend dirty tracking lives in apps/shared/src/backend/dirty_write.rs. It is
a conservative operation-level invalidation wrapper, not a file diff engine.
The backend is marked dirty after these successful operations:
write_bytescreate_dir_alltemp_pathexec, once it returnsOk(ExecResult), even if the process exit code is non-zero
Read-only operations do not mark the backend dirty:
statread_bytessearchexport
If a backend is clean and already has a BackendCheckpointRef, a new backend
checkpoint reuses that ref. If it is dirty or has no previous checkpoint ref,
the manager creates a new backend checkpoint and clears the dirty flag.
Checkout Versus Resume
checkout_runtime and resume_session are different operations:
| Operation | Input | Creates new id | Backend action | Use case |
|---|---|---|---|---|
checkout_runtime |
checkpoint_id |
Yes, a generated child runtime_id |
Optional backend checkout from BackendCheckpointRef |
Branch from a stable checkpoint |
pause_runtime |
existing runtime_id |
No | Snapshot then release the live backend | Free provider resources while preserving runtime state |
resume_runtime |
paused runtime_id |
No | Backend checkout from the paused checkpoint | Continue a paused runtime |
resume_session |
existing session_id |
No | No checkpoint or backend checkout | Reattach to an existing live session handle |
Checkout is non-destructive in v1. It copies the checkpointed session context,
optionally overrides conversation_id and sender_id, binds a checked-out
backend when the checkpoint has one, saves the child session, and registers the
checkpoint as the child runtime head.
Resume only returns the current snapshot of an existing session handle. It does not create a checkpoint, does not create a branch, and does not change backend placement.
Compatibility Notes
SessionForkRequest, SessionForkResult, and backend fork_backend shapes
still exist for compatibility and tests. Conceptually, fork is now modeled as:
checkpoint runtime -> checkout runtime
The HTTP router currently exposes the runtime checkpoint/checkout routes. New callers should use runtime checkpoint APIs directly instead of relying on a session-level fork concept.
Module Layout
- Runtime checkpoint API types live at
apps/shared/src/runtime_checkpoint.rsand are re-exported fromxiaoo_sharedcrate root. - Session-scoped runtime assembly and resolution live under
apps/shared/src/gateway/session_runtime/. - Backend manager types live under
apps/shared/src/backend/; shared backend request/result shapes are grouped inapps/shared/src/backend/base.rs. - Provider-specific code may still use provider-native words such as sandbox when talking to E2B or Conch. The xiaoO manager-facing layer uses backend and runtime terminology.