> ## Documentation Index
> Fetch the complete documentation index at: https://docs.microsandbox.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Snapshots

> Save files or running workloads and restore them into new sandboxes

<Tooltip tip="Cloud supports disk snapshots from stopped persistent sandboxes and disk restore. Full snapshots, forking, groups, and local archives are not yet available on cloud."><span className="msb-badge-limited">Limited on cloud <Icon icon="circle-info" size={11} /></span></Tooltip>

Save a prepared sandbox and reuse it as a starting point. Each restore creates a new sandbox and leaves the source unchanged.

## Snapshot types

Disk is the default. Full adds memory and execution state; there is no memory-only snapshot.

| Type | Saves | On restore |
| - | - | - |
| **Disk** | Files and owned volumes | Boots a new VM |
| **Full** | Disk, memory, and running processes | Resumes execution |

## Disk snapshots

Use disk snapshots for installed dependencies, prepared workspaces, and saved files. Cloud requires a stopped persistent source. See [guest flushing](#guest-flushing) for how microsandbox handles pending writes.

<Steps>
  <Step title="Prepare">
    Start a `python:3.12` sandbox named `baseline` using the [create guide](/sandboxes/lifecycle#create-a-sandbox), then install a dependency:

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const baseline = await Sandbox.get("baseline");

      const session = await baseline.connect();
      await session.exec("pip", ["install", "requests"]);

      await baseline.stop();
      ```

      ```rust Rust theme={null}
      let baseline = Sandbox::get("baseline").await?;

      let session = baseline.connect().await?;
      session.exec("pip", ["install", "requests"]).await?;

      baseline.stop().await?;
      ```

      ```python Python theme={null}
      baseline = await Sandbox.get("baseline")

      session = await baseline.connect()
      await session.exec("pip", ["install", "requests"])

      await baseline.stop()
      ```

      ```go Go theme={null}
      baseline, err := m.GetSandbox(ctx, "baseline")
      if err != nil { return err }

      session, err := baseline.Connect(ctx)
      if err != nil { return err }

      _, err = session.Exec(ctx, "pip", []string{"install", "requests"})
      if err != nil { return err }

      err = baseline.Stop(ctx)
      if err != nil { return err }
      ```

      ```ruby Ruby theme={null}
      baseline = Microsandbox::Sandbox.get("baseline")

      session = baseline.connect
      session.exec("pip", ["install", "requests"])

      baseline.stop
      ```

      ```bash CLI theme={null}
      msb exec baseline -- pip install requests
      msb stop baseline
      ```
    </CodeGroup>
  </Step>

  <Step title="Capture">
    Save the prepared state as `ready`:

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import { Sandbox } from "microsandbox";

      const baseline = await Sandbox.get("baseline");

      const snapshot = await baseline.snapshot("ready");
      ```

      ```rust Rust theme={null}
      use microsandbox::Sandbox;

      let baseline = Sandbox::get("baseline").await?;

      let snapshot = baseline.snapshot("ready").await?;
      ```

      ```python Python theme={null}
      from microsandbox import Sandbox

      baseline = await Sandbox.get("baseline")

      snapshot = await baseline.snapshot("ready")
      ```

      ```go Go theme={null}
      baseline, err := m.GetSandbox(ctx, "baseline")
      if err != nil {
          return err
      }

      snapshot, err := baseline.Snapshot(ctx, "ready")
      if err != nil {
          return err
      }
      ```

      ```ruby Ruby theme={null}
      baseline = Microsandbox::Sandbox.get("baseline")
      snapshot = baseline.snapshot("ready")
      ```

      ```bash CLI theme={null}
      msb snap create ready --sandbox baseline
      ```
    </CodeGroup>
  </Step>

  <Step title="Restore">
    Restore into `worker`. Its disk writes are independent of the source.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const worker = await Sandbox.restore(snapshot)
          .name("worker")
          .restore();
      ```

      ```rust Rust theme={null}
      let worker = Sandbox::restore_ref(snapshot.reference())
          .name("worker")
          .restore()
          .await?;
      ```

      ```python Python theme={null}
      worker = await Sandbox.restore(snapshot, name="worker")
      ```

      ```go Go theme={null}
      worker, err := m.RestoreSandbox(ctx, snapshot, "worker")
      if err != nil {
          return err
      }
      ```

      ```bash CLI theme={null}
      # Local snapshot selector
      msb snap restore baseline:ready --name worker
      msb exec worker -- python -c "import requests; print(requests.__version__)"
      ```
    </CodeGroup>
  </Step>
</Steps>

Local snapshots use `group:member` selectors, such as `baseline:ready`. For cloud, pass the snapshot object or reference; it has no client-local path.

## Full snapshots

<Tooltip tip="Full snapshots are not yet available on microsandbox cloud."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

Capture a running workload to resume its processes and memory later. A running source briefly pauses during capture, then resumes; an already-paused source stays paused.

On Windows, full snapshots now preserve all tracked hardlink names for bind mounts. Runtimes that support this format can still restore the previous Windows filesystem format. Older runtimes cannot restore snapshots written with the new bind-mount format; use the updated runtime when restoring them. This does not change disk-only snapshot compatibility.

<Steps>
  <Step title="Capture">
    Start with a running or paused sandbox named `worker`:

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import { Snapshot } from "microsandbox";

      const snapshot = await Snapshot.builder("snap")
          .fromSandbox("worker")
          .full()
          .create();
      ```

      ```rust Rust theme={null}
      use microsandbox::Snapshot;

      let snapshot = Snapshot::builder("snap")
          .from_sandbox("worker")
          .full()
          .create()
          .await?;
      ```

      ```python Python theme={null}
      from microsandbox import Snapshot

      snapshot = await Snapshot.create(
          "snap",
          from_sandbox="worker",
          full=True,
      )
      ```

      ```go Go theme={null}
      snapshot, err := m.Snapshot.Create(ctx, m.SnapshotCreateOptions{
          Name:        "snap",
          FromSandbox: "worker",
          Full:        true,
      })
      ```

      ```bash CLI theme={null}
      msb snap create snap --sandbox worker --full
      ```
    </CodeGroup>
  </Step>

  <Step title="Restore">
    Resume in a new sandbox. The image’s startup command does not run again.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const resumed = await Sandbox.restore("worker:snap")
          .name("resumed")
          .restore();
      ```

      ```rust Rust theme={null}
      let resumed = Sandbox::restore("worker:snap")
          .name("resumed")
          .restore()
          .await?;
      ```

      ```python Python theme={null}
      resumed = await Sandbox.restore("worker:snap", name="resumed")
      ```

      ```go Go theme={null}
      resumed, err := m.RestoreSandbox(ctx, "worker:snap", "resumed")
      if err != nil { return err }
      ```

      ```bash CLI theme={null}
      msb snap restore worker:snap --name resumed
      ```
    </CodeGroup>
  </Step>
</Steps>

* **Compatibility:** Use a compatible runtime and guest kernel. CPU and memory settings, including maximums, must match the capture.
* **Connections:** Reconnect clients and application connections. Microsandbox does not capture host-side connections or transfers, and does not replay buffered process output.
* **Mounts:** Owned volumes are private to each child. Host bindings need explicit authorization; see [mount behavior](#mount-behavior).

<Accordion title="Capture or restore fails">
  If capture saves an artifact but cannot recover the source, inspect the artifact reported in the error before retrying. A packaging failure may leave only a runtime checkpoint.

  If restore leaves an incomplete child, remove it and restore again; the snapshot remains reusable. Older development full snapshots may need recapture or disk-only restore.
</Accordion>

### Share memory

Full restores normally copy memory. Enable copy-on-write (CoW) to share unchanged pages between children while keeping writes private:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const worker_a = await Sandbox.restore("worker:snap")
      .name("worker_a")
      .cowMemory()
      .restore();

  const worker_b = await Sandbox.restore("worker:snap")
      .name("worker_b")
      .cowMemory()
      .restore();
  ```

  ```rust Rust theme={null}
  let worker_a = Sandbox::restore("worker:snap")
      .name("worker_a")
      .cow_memory()
      .restore()
      .await?;

  let worker_b = Sandbox::restore("worker:snap")
      .name("worker_b")
      .cow_memory()
      .restore()
      .await?;
  ```

  ```python Python theme={null}
  worker_a = await Sandbox.restore(
      "worker:snap",
      name="worker_a",
      cow_memory=True,
  )

  worker_b = await Sandbox.restore(
      "worker:snap",
      name="worker_b",
      cow_memory=True,
  )
  ```

  ```go Go theme={null}
  worker_a, err := m.RestoreSandbox(ctx, "worker:snap", "worker_a",
      m.WithCowMemory(),
  )
  if err != nil { return err }

  worker_b, err := m.RestoreSandbox(ctx, "worker:snap", "worker_b",
      m.WithCowMemory(),
  )
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb snap restore worker:snap --name worker_a --cow-mem
  msb snap restore worker:snap --name worker_b --cow-mem
  ```
</CodeGroup>

The first restore prepares a local memory cache; later restores reuse it. No special source setup is needed. CoW requires a full snapshot and cannot be combined with disk-only restore.

<span id="migrating-restore-options" />

<Accordion title="Older restore option names">
  The old `forked` options still enable copy-on-write memory during restore. Use these names in new code:

  | Deprecated name | Preferred name |
  | - | - |
  | CLI `--forked` | `--cow-mem` |
  | TypeScript `.forked()` | `.cowMemory()` |
  | Rust `.forked()` | `.cow_memory()` |
  | Python `forked=True` | `cow_memory=True` |
  | Go `WithForked()` / `RestoreConfig.Forked` | `WithCowMemory()` / `RestoreConfig.CowMemory` |

  The MCP `sandbox_restore` tool retains its `forked` input for the same memory option.
</Accordion>

### Restore disk only

Boot a fresh VM from a full snapshot’s saved disk without resuming its processes. You can choose different CPU and memory settings.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const rebooted = await Sandbox.restore("worker:snap")
      .name("rebooted")
      .diskOnly()
      .restore();
  ```

  ```rust Rust theme={null}
  let rebooted = Sandbox::restore("worker:snap")
      .name("rebooted")
      .disk_only()
      .restore()
      .await?;
  ```

  ```python Python theme={null}
  rebooted = await Sandbox.restore(
      "worker:snap",
      name="rebooted",
      disk_only=True,
  )
  ```

  ```go Go theme={null}
  rebooted, err := m.RestoreSandbox(ctx, "worker:snap", "rebooted",
      m.WithSnapshotDiskOnly(),
  )
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb snap restore worker:snap --name rebooted --disk-only
  ```
</CodeGroup>

Snapshots with a tmpfs root cannot be restored disk-only.

### Keep the guest clock

By default (`sync`), the sandbox's date and time follow the host. The host updates the clock at boot, about once a minute, and when a full snapshot restores or a paused sandbox resumes.

With `off`, the host stops updating the clock after boot. Full restores continue from the snapshot's saved time. The clock keeps running, making this useful for tests that need a repeatable starting time.

<CodeGroup>
  ```rust Rust theme={null}
  use microsandbox::sandbox::GuestClockPolicy;

  let source = Sandbox::builder("worker")
      .image("alpine")
      .guest_clock(GuestClockPolicy::Off)
      .create()
      .await?;
  ```

  ```bash CLI theme={null}
  msb create alpine --name worker --guest-clock off
  ```
</CodeGroup>

Full snapshots save this setting. Restores inherit it unless you choose `sync` to use host time or `off` to continue from the saved time:

<CodeGroup>
  ```rust Rust theme={null}
  let child = Sandbox::restore("worker:snap")
      .name("child")
      .guest_clock(GuestClockPolicy::Sync)
      .restore()
      .await?;
  ```

  ```bash CLI theme={null}
  msb snap restore worker:snap --name child --guest-clock sync
  ```
</CodeGroup>

* **Scope:** `off` prevents host clock updates. Programs inside the sandbox with permission to set the time can still change it.
* **Drift:** With `off`, the guest clock is not corrected after the host sleeps or its clock changes.
* **Monotonic clock:** Continues from the snapshot in both modes.
* **Forks:** Forks inherit this setting.
* **Compatibility:** Older runtimes reject `off`. Older SDKs and CLIs reject full snapshots saved with `off`, including exported archives. Cloud also rejects `off`.
* **Downgrades:** Older SDKs and CLIs require a database downgrade to reopen this installation. Downgrading is blocked while any sandbox's saved or active settings use `off`.

<a id="guest-filesystem-flush" />

### Guest flushing

Guest flushing writes pending filesystem changes from memory to disk. It works the same way in the CLI and all SDKs, for snapshots, forking, and pausing.

The default `auto` policy flushes running disk snapshots. Full snapshots and forks keep those changes in memory instead. Choose `required` if you plan to restore a full snapshot's disks without its memory:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { Snapshot } from "microsandbox";

  const snapshot = await Snapshot.builder("ready")
      .fromSandbox("worker")
      .full()
      .guestFlush("required")
      .create();
  ```

  ```rust Rust theme={null}
  use microsandbox::{snapshot::GuestFlush, Snapshot};

  let snapshot = Snapshot::builder("ready")
      .from_sandbox("worker")
      .full()
      .guest_flush(GuestFlush::Required)
      .create()
      .await?;
  ```

  ```python Python theme={null}
  from microsandbox import GuestFlush, Snapshot

  snapshot = await Snapshot.create(
      "ready",
      from_sandbox="worker",
      full=True,
      guest_flush=GuestFlush.REQUIRED,
  )
  ```

  ```go Go theme={null}
  import m "github.com/superradcompany/microsandbox/sdk/go"

  snapshot, err := m.Snapshot.Create(ctx, m.SnapshotCreateOptions{
      Name:        "ready",
      FromSandbox: "worker",
      Full:        true,
      GuestFlush:  m.GuestFlushRequired,
  })
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb snap create ready --sandbox worker --full --guest-flush required
  ```
</CodeGroup>

Flushing does not save data still buffered by your application or commit database transactions. Save or commit that data before capturing the snapshot.

<Accordion title="Advanced flush behavior">
  | Policy | Behavior |
  | - | - |
  | `auto` (default) | Flush before a running disk-only capture; no extra flush for full captures, forking, or pausing |
  | `required` | Require a successful flush before capture or pause |
  | `skip` | Skip guest flushing; disk-only snapshots may miss buffered writes and need filesystem recovery |

  * **Paused sandboxes:** Pause with `required` before taking a disk snapshot. Capture fails if the needed flush was not done before pausing; it never resumes the sandbox to flush.
  * **Stopped or crashed sandboxes:** `auto` and `skip` capture the saved disk state. `required` fails because no guest is running.
  * **Storage:** Flushing covers the root and captured block disks, including owned disks. It does not make tmpfs persistent. Host storage synchronization and snapshot durability remain enforced with every policy.
  * **Compatibility:** Older runtimes may reject the policy; restart the sandbox with an updated runtime. Cloud supports only `auto`.
</Accordion>

<span id="branching" />

## Forking

<Tooltip tip="Forking is not yet available on microsandbox cloud."><span className="msb-badge-local">Local-only <Icon icon="circle-info" size={11} /></span></Tooltip>

Create a child directly from a running or paused sandbox, without saving a reusable snapshot. Forking preserves disk and execution state. Memory sharing is built in; each child’s writes stay private. To start from a saved snapshot, use [restore](#full-snapshots).

The former `branch` SDK methods remain deprecated aliases for `fork`.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const source = await Sandbox.get("worker");

  const experiment = await source.fork("experiment");
  ```

  ```rust Rust theme={null}
  let source = Sandbox::get("worker").await?;

  let experiment = source.fork("experiment").fork().await?;
  ```

  ```python Python theme={null}
  source = await Sandbox.get("worker")

  experiment = await source.fork("experiment")
  ```

  ```go Go theme={null}
  source, err := m.GetSandbox(ctx, "worker")
  if err != nil { return err }

  experiment, err := source.Fork(ctx, "experiment")
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb fork worker --name experiment
  ```
</CodeGroup>

The source returns to its previous running or paused state. Each child needs a new name and cannot inherit published host ports. Host mounts follow the same [binding rules](#external-mounts).

<Accordion title="Create several children">
  Capture once for several children:

  <CodeGroup>
    ```typescript TypeScript theme={null}
    const source = await Sandbox.get("worker");

    const outcomes = await source.forkMany(["alice", "bob"]);
    for (const outcome of outcomes) {
      if (outcome.error !== undefined) {
        console.error(`${outcome.name}: ${outcome.error.message}`);
      } else {
        console.log(`${outcome.name} started`);
        // Use the child, then stop it when finished.
        await outcome.sandbox.stop();
      }
    }
    ```

    ```rust Rust theme={null}
    let source = Sandbox::get("worker").await?;

    let outcomes = source.fork_many(["alice", "bob"]).fork().await?;
    for outcome in outcomes {
        match outcome.result {
            Ok(child) => {
                println!("{} started", outcome.name);
                // Use the child, then stop it when finished.
                child.stop().await?;
            }
            Err(error) => eprintln!("{}: {error}", outcome.name),
        }
    }
    ```

    ```python Python theme={null}
    source = await Sandbox.get("worker")

    outcomes = await source.fork_many(["alice", "bob"])
    for outcome in outcomes:
        if outcome.error is not None:
            print(f"{outcome.name}: {outcome.error}")
        else:
            print(f"{outcome.name} started")
            # Use the child, then stop it when finished.
            await outcome.sandbox.stop()
    ```

    ```go Go theme={null}
    source, err := m.GetSandbox(ctx, "worker")
    if err != nil { return err }

    outcomes, err := source.ForkMany(ctx, []string{"alice", "bob"})
    if err != nil { return err }
    for _, outcome := range outcomes {
        if outcome.Error != nil {
            fmt.Printf("%s: %v\n", outcome.Name, outcome.Error)
            continue
        }
        fmt.Printf("%s started\n", outcome.Name)
        // Use the child, then stop it and close its handle when finished.
        stopErr := outcome.Sandbox.Stop(ctx)
        outcome.Sandbox.Close()
        if stopErr != nil { return stopErr }
    }
    ```

    ```bash CLI theme={null}
    msb fork worker --names alice bob
    ```
  </CodeGroup>

  Each child gets its own result; a failed child does not remove successful siblings. Validation or capture errors fail the call. Deleting the source or a sibling does not invalidate other children.
</Accordion>

## Mount behavior

| State | Disk snapshot | Full snapshot |
| - | - | - |
| Owned volume contents | Private copy per child | Private copy per child |
| Host bind-mount files | External; not copied | External; not copied |
| Open handles and caches | Processes start fresh | Supported state resumes |

Owned directory snapshots cannot move between Unix and Windows. Owned disks do not have this restriction.

### External mounts

Snapshots do not include host-mounted files. Supply their host paths when restoring. Owned volumes restore automatically. Full restore requires every captured external filesystem and additional disk to be available.

For either a disk or full snapshot, mount a host directory like this:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const child = await Sandbox.restore("worker:snap")
      .name("child")
      .volume("/work", v => v.bind("/host/project"))
      .restore();
  ```

  ```rust Rust theme={null}
  let child = Sandbox::restore("worker:snap")
      .name("child")
      .volume("/work", |v| v.bind("/host/project"))
      .restore()
      .await?;
  ```

  ```python Python theme={null}
  from microsandbox import Sandbox, Volume

  child = await Sandbox.restore(
      "worker:snap",
      name="child",
      volumes={"/work": Volume.bind("/host/project")},
  )
  ```

  ```go Go theme={null}
  child, err := m.RestoreSandbox(ctx, "worker:snap", "child",
      m.WithRestoreConfig(m.RestoreConfig{
          Volumes: map[string]m.MountConfig{
              "/work": m.Mount.Bind("/host/project", m.MountOptions{}),
          },
      }))
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb snap restore worker:snap --name child -v /host/project:/work
  ```
</CodeGroup>

Using the same host directory for the source and restored sandbox shares its files.

<Accordion title="Advanced restore options">
  * **Reuse source bindings:** Restore on the same host using the source's validated mounts. Missing dependencies still cause restore to fail.
  * **Select a captured disk:** Restore a private copy of a named disk volume. External disk images are not converted to owned volumes.
  * **Allow missing resources:** Continue without an external filesystem or additional disk. Root and owned storage must still be complete. Access to missing resources fails and may disrupt applications or make filesystems read-only.

  Each example below restores a separate sandbox using one of these options:

  <CodeGroup>
    ```typescript TypeScript theme={null}
    // Reuse source bindings.
    const inherited = await Sandbox.restore("worker:snap")
        .name("inherited")
        .dangerouslyInheritResources()
        .restore();

    // Select a captured disk.
    const privateDisk = await Sandbox.restore("worker:snap")
        .name("private-disk")
        .volume("/data", v => v.captured())
        .restore();

    // Allow missing resources.
    const partial = await Sandbox.restore("worker:snap")
        .name("partial")
        .allowMissingResources()
        .restore();
    ```

    ```rust Rust theme={null}
    // Reuse source bindings.
    let inherited = Sandbox::restore("worker:snap")
        .name("inherited")
        .dangerously_inherit_resources()
        .restore()
        .await?;

    // Select a captured disk.
    let private_disk = Sandbox::restore("worker:snap")
        .name("private-disk")
        .volume("/data", |v| v.captured())
        .restore()
        .await?;

    // Allow missing resources.
    let partial = Sandbox::restore("worker:snap")
        .name("partial")
        .allow_missing_resources()
        .restore()
        .await?;
    ```

    ```python Python theme={null}
    # Reuse source bindings.
    inherited = await Sandbox.restore(
        "worker:snap", name="inherited", dangerously_inherit_resources=True,
    )

    # Select a captured disk.
    private_disk = await Sandbox.restore(
        "worker:snap", name="private-disk", captured_volumes=["/data"],
    )

    # Allow missing resources.
    partial = await Sandbox.restore(
        "worker:snap", name="partial", allow_missing_resources=True,
    )
    ```

    ```go Go theme={null}
    // Reuse source bindings.
    inherited, err := m.RestoreSandbox(ctx, "worker:snap", "inherited",
        m.WithDangerouslyInheritResources())
    if err != nil { return err }

    // Select a captured disk.
    privateDisk, err := m.RestoreSandbox(ctx, "worker:snap", "private-disk",
        m.WithRestoreConfig(m.RestoreConfig{CapturedVolumes: []string{"/data"}}))
    if err != nil { return err }

    // Allow missing resources.
    partial, err := m.RestoreSandbox(ctx, "worker:snap", "partial",
        m.WithAllowMissingResources())
    if err != nil { return err }
    ```

    ```bash CLI theme={null}
    # Reuse source bindings.
    msb snap restore worker:snap --name inherited --dangerously-inherit-resources

    # Select a captured disk.
    msb snap restore worker:snap --name private-disk -v /data

    # Allow missing resources.
    msb snap restore worker:snap --name partial --allow-missing-resources
    ```
  </CodeGroup>

  Allowing missing resources does not reuse source bindings or accept changed files; see [Validate mounts](#validate-mounts). Warnings remain visible even when progress output is suppressed.

  Direct forking keeps its existing behavior for unavailable resources. Disk-only restore boots fresh rather than resuming open filesystem handles.
</Accordion>

### Validate mounts

Full restore and forking validate captured filesystem identities. Strict is the default; relaxed mode accepts supported differences and reports warnings:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const child = await Sandbox.restore("worker:snap")
      .name("child")
      .volume("/work", v => v.bind("/host/copy"))
      .externalMountPolicy("relaxed")
      .restore();
  ```

  ```rust Rust theme={null}
  use microsandbox::ExternalMountRestorePolicy;

  let child = Sandbox::restore("worker:snap")
      .name("child")
      .volume("/work", |v| v.bind("/host/copy"))
      .external_mount_policy(ExternalMountRestorePolicy::Relaxed)
      .restore()
      .await?;
  ```

  ```python Python theme={null}
  from microsandbox import Sandbox, Volume

  child = await Sandbox.restore(
      "worker:snap",
      name="child",
      volumes={"/work": Volume.bind("/host/copy")},
      external_mount_policy="relaxed",
  )
  ```

  ```go Go theme={null}
  child, err := m.RestoreSandbox(ctx, "worker:snap", "child",
      m.WithRestoreConfig(m.RestoreConfig{
          Volumes: map[string]m.MountConfig{
              "/work": m.Mount.Bind("/host/copy", m.MountOptions{}),
          },
      }),
      m.WithExternalMountPolicy(m.ExternalMountRelaxed))
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb snap restore worker:snap --name child \
      -v /host/copy:/work --external-mount-policy relaxed
  ```
</CodeGroup>

Relaxed mode accepts supported changes to supplied filesystem objects. It still checks integrity and requires access to the supplied paths. It does not recreate missing files or allow missing resources.

<Accordion title="Open files and cached data">
  Full snapshots retain clean cache pages, so reads may return captured bytes before reaching the host. Strict validation checks identity at restore time; it does not lock files against later edits.

  External files deleted while still open, and special files, can prevent capture. Owned directories support deleted open regular files. Restore warnings identify inaccessible or changed objects; see your [SDK reference](#reference) for details.
</Accordion>

## Manage snapshots

These operations apply to disk and full snapshots. Cloud supports basic disk snapshot management; groups, archives, verification, and disk maintenance below are local-only.

### Restore progress

Track local disk or full restore through image preparation, memory preparation, and activation. Wait for the final result before using the sandbox:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const creation = await Sandbox.restore("worker:snap")
      .name("resumed")
      .restoreWithProgress();

  for await (const event of creation) {
      console.log(event);
  }

  const resumed = await creation.awaitSandbox();
  ```

  ```rust Rust theme={null}
  let (mut progress, task) = Sandbox::restore("worker:snap")
      .name("resumed")
      .restore_with_progress()?;

  while let Some(event) = progress.recv().await {
      eprintln!("{event:?}");
  }

  let resumed = task.await??;
  ```

  ```python Python theme={null}
  creation = Sandbox.restore_with_progress(
      "worker:snap",
      name="resumed",
  )

  async for event in creation.progress:
      print(event)

  resumed = await creation.result()
  ```

  ```go Go theme={null}
  events, results := m.RestoreSandboxWithProgress(
      ctx, "worker:snap", "resumed",
  )

  for event := range events {
      fmt.Println(event)
  }

  result := <-results
  if result.Err != nil { return result.Err }
  resumed := result.Sandbox
  defer resumed.Close()
  ```

  ```bash CLI theme={null}
  # Restore shows progress by default.
  msb snap restore worker:snap --name resumed
  ```
</CodeGroup>

In Go, always receive the result and close its sandbox handle when finished, even if you ignore progress events. Closing a restored handle does not stop the detached sandbox; call `Stop` or `Destroy` when needed. Cancel the context to cancel the operation.

Some stages have no percentage. Memory preparation has no fixed timeout; activation has a separate timeout. Set an overall deadline or use SDK cancellation when needed.

### Manage saved snapshots

List snapshots, inspect one, or remove it:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const snapshots = await Snapshot.list();

  const saved = await Snapshot.open("baseline:ready");
  console.log(saved.digest);

  await Snapshot.remove("baseline:ready");
  ```

  ```rust Rust theme={null}
  let snapshots = Snapshot::list().await?;

  let saved = Snapshot::open("baseline:ready").await?;
  println!("{}", saved.digest());

  Snapshot::remove("baseline:ready", false).await?;
  ```

  ```python Python theme={null}
  snapshots = await Snapshot.list()

  saved = await Snapshot.open("baseline:ready")
  print(saved.digest)

  await Snapshot.remove("baseline:ready")
  ```

  ```go Go theme={null}
  snapshots, err := m.Snapshot.List(ctx)
  if err != nil { return err }

  saved, err := m.Snapshot.Open(ctx, "baseline:ready")
  if err != nil { return err }

  fmt.Println(saved.Digest())
  err = m.Snapshot.Remove(ctx, "baseline:ready", false)
  if err != nil { return err }
  ```

  ```ruby Ruby theme={null}
  snapshots = Microsandbox::Snapshot.list

  saved = Microsandbox::Snapshot.open("baseline:ready")
  puts saved.digest

  Microsandbox::Snapshot.remove("baseline:ready")
  ```

  ```bash CLI theme={null}
  msb snap ls
  msb snap ls --group baseline
  msb snap inspect baseline:ready
  msb snap rm baseline:ready
  ```
</CodeGroup>

<span id="snapshot-history-and-branching" />

### Snapshot groups

A group keeps related snapshots together. Restore an earlier snapshot and save new changes to explore a different path while keeping the original snapshots. Choose the original group when capturing to keep the new snapshots in that group; otherwise, the group defaults to the restored sandbox’s name.

Since v0.7, installed snapshot members are immutable. Creating another snapshot cannot overwrite an existing member. In SDK calls, omit `force` or set it to `false` (`False` in Python): enabling it fails even when the requested name does not exist. Use a unique or generated member name for each capture. To reuse a name, explicitly remove the existing member first; removal still checks for dependent snapshots.

See the [v0.6 snapshot upgrade example](/migrations/v0.7#update-snapshot-code).

Locally, `group` selects its head; `group:member` selects an exact snapshot:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await Snapshot.builder("cp01")
      .fromSandbox("baseline")
      .group("work")
      .create();

  await Snapshot.builder("cp02")
      .fromSandbox("baseline")
      .group("work")
      .create();

  const latest = await Sandbox.restore("work")
      .name("latest")
      .restore();

  const earlier = await Sandbox.restore("work:cp01")
      .name("earlier")
      .restore();

  await Snapshot.groupHead("work");

  await Snapshot.groupHead("work:cp01");
  ```

  ```rust Rust theme={null}
  Snapshot::builder("cp01")
      .from_sandbox("baseline")
      .group("work")
      .create()
      .await?;

  Snapshot::builder("cp02")
      .from_sandbox("baseline")
      .group("work")
      .create()
      .await?;

  let latest = Sandbox::restore("work")
      .name("latest")
      .restore()
      .await?;

  let earlier = Sandbox::restore("work:cp01")
      .name("earlier")
      .restore()
      .await?;

  Snapshot::group_head("work").await?;

  Snapshot::group_head("work:cp01").await?;
  ```

  ```python Python theme={null}
  await Snapshot.create("cp01", from_sandbox="baseline", group="work")

  await Snapshot.create("cp02", from_sandbox="baseline", group="work")

  latest = await Sandbox.restore("work", name="latest")

  earlier = await Sandbox.restore("work:cp01", name="earlier")

  await Snapshot.group_head("work")

  await Snapshot.group_head("work:cp01")
  ```

  ```go Go theme={null}
  var err error

  _, err = m.Snapshot.Create(ctx, m.SnapshotCreateOptions{
      Name: "cp01",
      FromSandbox: "baseline",
      Group: "work",
  })
  if err != nil { return err }

  _, err = m.Snapshot.Create(ctx, m.SnapshotCreateOptions{
      Name: "cp02",
      FromSandbox: "baseline",
      Group: "work",
  })
  if err != nil { return err }

  latest, err := m.RestoreSandbox(ctx, "work", "latest")
  if err != nil { return err }

  earlier, err := m.RestoreSandbox(ctx, "work:cp01", "earlier")
  if err != nil { return err }

  _, err = m.Snapshot.GroupHead(ctx, "work")
  if err != nil { return err }

  _, err = m.Snapshot.GroupHead(ctx, "work:cp01")
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb snap create cp01 --sandbox baseline --group work
  msb snap create cp02 --sandbox baseline --group work
  msb snap restore work --name latest
  msb snap restore work:cp01 --name earlier
  msb snap head work           # Show the selected snapshot ID
  msb snap head work:cp01      # Explicitly select an earlier checkpoint
  ```
</CodeGroup>

The first capture sets the head. Later captures or imports advance it only with proven ancestry; older or divergent snapshots leave it unchanged. Select another head before removing the current one if other members remain.

<Accordion title="Import into a group">
  Imports without a group create a new one. Select an imported head explicitly when needed:

  <CodeGroup>
    ```typescript TypeScript theme={null}
    await Snapshot.loadWithOptions("snap.msb", { group: "work" });

    await Snapshot.loadWithOptions("experiment.msb", {
        group: "work",
        setHead: true,
    });
    ```

    ```rust Rust theme={null}
    use std::path::Path;

    use microsandbox::{snapshot::LoadOpts, Snapshot};

    Snapshot::load_with_options(
        Path::new("snap.msb"),
        LoadOpts {
            group: Some("work".into()),
            ..Default::default()
        },
    ).await?;

    Snapshot::load_with_options(
        Path::new("experiment.msb"),
        LoadOpts {
            group: Some("work".into()),
            set_head: true,
            ..Default::default()
        },
    ).await?;
    ```

    ```python Python theme={null}
    await Snapshot.load("snap.msb", group="work")

    await Snapshot.load("experiment.msb", group="work", set_head=True)
    ```

    ```go Go theme={null}
    var err error

    _, err = m.Snapshot.LoadWithOptions(ctx, "snap.msb",
        m.SnapshotLoadOptions{Group: "work"},
    )
    if err != nil { return err }

    _, err = m.Snapshot.LoadWithOptions(ctx, "experiment.msb",
        m.SnapshotLoadOptions{Group: "work", SetHead: true},
    )
    if err != nil { return err }
    ```

    ```bash CLI theme={null}
    msb snap import snap.msb --group work
    msb snap import experiment.msb --group work --set-head
    ```
  </CodeGroup>

  Identical members can be reused; conflicting IDs or names fail. Publication never overwrites an existing member. Inspect the group before retrying an interrupted import.
</Accordion>

## Move snapshots

Archives let you move local disk or full snapshots between machines.

### Export and restore

Copy either snapshot type to another machine as a `.msb` archive. Include the image for offline restore:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await Snapshot.save("baseline:ready", "ready.msb", { withImage: true });
  ```

  ```rust Rust theme={null}
  use std::path::Path;

  use microsandbox::{snapshot::SaveOpts, Snapshot};

  Snapshot::save("baseline:ready",
      Path::new("ready.msb"),
      SaveOpts {
          with_image: true,
          ..Default::default()
      },
  ).await?;
  ```

  ```python Python theme={null}
  await Snapshot.save("baseline:ready", "ready.msb", with_image=True)
  ```

  ```go Go theme={null}
  var err error

  err = m.Snapshot.Save(ctx, "baseline:ready", "ready.msb",
      m.SnapshotSaveOptions{WithImage: true},
  )
  if err != nil { return err }
  ```

  ```ruby Ruby theme={null}
  Microsandbox::Snapshot.save("baseline:ready", "ready.msb", with_image: true)
  ```

  ```bash CLI theme={null}
  msb snap export baseline:ready --output ready.msb --with-image
  ```
</CodeGroup>

On the destination:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const worker = await Sandbox.restore("./ready.msb")
      .name("worker")
      .restore();
  ```

  ```rust Rust theme={null}
  let worker = Sandbox::restore("./ready.msb")
      .name("worker")
      .restore()
      .await?;
  ```

  ```python Python theme={null}
  worker = await Sandbox.restore("./ready.msb", name="worker")
  ```

  ```go Go theme={null}
  worker, err := m.RestoreSandbox(ctx, "./ready.msb", "worker")
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb snap restore ./ready.msb --name worker
  ```
</CodeGroup>

The archive keeps its type: disk boots fresh; full resumes execution. Existing `.tar` and `.tar.zst` archives also work. Full compatibility and owned-directory platform restrictions still apply.

### Import archives

Load a base and dependent archives together; order does not matter:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const handles = await Snapshot.loadMany(
      ["changes.msb", "base.msb"],
      { group: "received", dest: "/mnt/snapshots" },
  );
  ```

  ```rust Rust theme={null}
  use microsandbox::{snapshot::LoadOpts, Snapshot};

  let archives = vec!["changes.msb".into(), "base.msb".into()];

  let handles = Snapshot::load_many(
      &archives,
      LoadOpts {
          group: Some("received".into()),
          dest: Some("/mnt/snapshots".into()),
          ..Default::default()
      },
  ).await?;
  ```

  ```python Python theme={null}
  handles = await Snapshot.load_many(
      ["changes.msb", "base.msb"],
      group="received",
      dest="/mnt/snapshots",
  )
  ```

  ```go Go theme={null}
  handles, err := m.Snapshot.LoadMany(ctx,
      []string{"changes.msb", "base.msb"},
      m.SnapshotLoadOptions{Group: "received", Dest: "/mnt/snapshots"},
  )
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb snap import changes.msb base.msb --group received --dest /mnt/snapshots
  ```
</CodeGroup>

Dependencies come from the batch or matching members of the named group. Supply an explicit base for other dependencies; unrelated groups are not searched. All members are validated before publication. Divergent tips require choosing a head; forcing an ambiguous head fails.

<span id="export-changes-and-compact-a-disk-chain" />

### Export changes

Export only disk layers and full-snapshot RAM objects not supplied by an exact base:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await Snapshot.builder("v2")
      .fromSandbox("worker")
      .full()
      .create();

  await Snapshot.save("worker:v2", "changes.msb", {
      since: "worker:v1",
  });
  ```

  ```rust Rust theme={null}
  use std::path::Path;

  use microsandbox::{snapshot::SaveOpts, Snapshot};

  Snapshot::builder("v2")
      .from_sandbox("worker")
      .full()
      .create()
      .await?;

  Snapshot::save("worker:v2",
      Path::new("changes.msb"),
      SaveOpts {
          since: Some("worker:v1".into()),
          ..Default::default()
      },
  ).await?;
  ```

  ```python Python theme={null}
  await Snapshot.create("v2", from_sandbox="worker", full=True)

  await Snapshot.save(
      "worker:v2",
      "changes.msb",
      since="worker:v1",
  )
  ```

  ```go Go theme={null}
  var err error

  _, err = m.Snapshot.Create(ctx, m.SnapshotCreateOptions{
      Name: "v2",
      FromSandbox: "worker",
      Full: true,
  })
  if err != nil { return err }

  err = m.Snapshot.Save(ctx, "worker:v2", "changes.msb",
      m.SnapshotSaveOptions{Since: "worker:v1"},
  )
  if err != nil { return err }
  ```

  ```ruby Ruby theme={null}
  # Export an existing snapshot against its saved base.
  Microsandbox::Snapshot.save(
    "worker:v2",
    "changes.msb",
    since: "worker:v1"
  )
  ```

  ```bash CLI theme={null}
  msb snap create v2 --sandbox worker --full
  msb snap export worker:v2 --output changes.msb --since worker:v1
  ```
</CodeGroup>

The root and every shared owned disk must match the base’s physical prefix. New owned disks are complete. Owned directory namespaces and full execution maps remain complete; unchanged payloads may come from the base.

Import or restore with that base:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await Snapshot.loadWithOptions("changes.msb", {
      group: "imported",
      base: "worker:v1",
  });

  const child = await Sandbox.restore("changes.msb")
      .name("child")
      .snapshotBase("worker:v1")
      .restore();
  ```

  ```rust Rust theme={null}
  use std::path::Path;

  use microsandbox::{snapshot::LoadOpts, Sandbox, Snapshot};

  Snapshot::load_with_options(
      Path::new("changes.msb"),
      LoadOpts {
          group: Some("imported".into()),
          base: Some("worker:v1".into()),
          ..Default::default()
      },
  ).await?;

  let child = Sandbox::restore("changes.msb")
      .name("child")
      .snapshot_base("worker:v1")
      .restore()
      .await?;
  ```

  ```python Python theme={null}
  await Snapshot.load(
      "changes.msb",
      group="imported",
      base="worker:v1",
  )

  child = await Sandbox.restore(
      "changes.msb",
      name="child",
      snapshot_base="worker:v1",
  )
  ```

  ```go Go theme={null}
  var err error

  _, err = m.Snapshot.LoadWithOptions(ctx, "changes.msb",
      m.SnapshotLoadOptions{Group: "imported", Base: "worker:v1"},
  )
  if err != nil { return err }

  child, err := m.RestoreSandbox(ctx, "changes.msb", "child",
      m.WithSnapshotBase("worker:v1"),
  )
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb snap import changes.msb --base worker:v1 --group imported
  msb snap restore changes.msb --name child --snapshot-base worker:v1
  ```
</CodeGroup>

Missing dependencies fail before publication or execution. Once loaded or restored, the result owns its files and no longer depends on the base.

<Accordion title="Other export options">
  The last-layers option exports only the newest root layers, keeping owned disks and required RAM complete. Do not combine it with since-base export, or use either with parent-chain or whole-group export.

  After compaction, export a new standalone baseline. Independent stopped captures can also get new layer identities despite identical bytes; use a standalone export when the base no longer matches.
</Accordion>

### Capture to an archive

Capture directly to an archive without installing a snapshot locally. Disk is the default; add the full option for execution state:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { Snapshot } from "microsandbox";

  const archive = await Snapshot.builder("deps")
      .fromSandbox("baseline")
      .createArchive("/tmp/deps.msb");
  ```

  ```rust Rust theme={null}
  use microsandbox::Snapshot;

  let archive = Snapshot::builder("deps")
      .from_sandbox("baseline")
      .create_archive("/tmp/deps.msb", false)
      .await?;
  ```

  ```python Python theme={null}
  from microsandbox import Snapshot

  archive = await Snapshot.create_archive(
      "deps",
      "/tmp/deps.msb",
      from_sandbox="baseline",
  )
  ```

  ```go Go theme={null}
  archive, err := m.Snapshot.CreateArchive(ctx, m.SnapshotArchiveOptions{
      SnapshotCreateOptions: m.SnapshotCreateOptions{
          Name:        "deps",
          FromSandbox: "baseline",
      },
      ArchivePath: "/tmp/deps.msb",
  })
  ```

  ```bash CLI theme={null}
  msb snap create deps \
      --sandbox baseline \
      -o /tmp/deps.msb
  ```
</CodeGroup>

Direct capture pins the image but does not bundle its cache. For offline use, create an installed snapshot and [export with the image](#export-and-restore). Restore the archive directly using the same restore APIs.

## Compact disks

Merge sealed disk layers in a running or fully stopped local sandbox. This affects disk storage, including disks in full snapshots, not memory. Preview before applying:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const worker = await Sandbox.get("worker");

  const plan = await worker.compact({ layers: 3, dryRun: true });

  const result = await worker.compact({ layers: 3 });
  ```

  ```rust Rust theme={null}
  let worker = Sandbox::get("worker").await?;

  let plan = worker.compact().layers(3).dry_run().await?;

  let result = worker.compact().layers(3).apply().await?;
  ```

  ```python Python theme={null}
  worker = await Sandbox.get("worker")

  plan = await worker.compact(layers=3, dry_run=True)

  result = await worker.compact(layers=3)
  ```

  ```go Go theme={null}
  worker, err := m.GetSandbox(ctx, "worker")
  if err != nil { return err }

  layers := uint32(3)
  plan, err := worker.Compact(ctx, m.DiskCompactionOptions{
      Layers: &layers,
      DryRun: true,
  })
  if err != nil { return err }

  result, err := worker.Compact(ctx, m.DiskCompactionOptions{Layers: &layers})
  if err != nil { return err }
  ```

  ```bash CLI theme={null}
  msb modify worker --compact --layers 3 --dry-run
  msb modify worker --compact --layers 3
  ```
</CodeGroup>

The default covers the root and owned data disks, excluding named/external volumes and directories. The layer limit includes the base but never the writable head; it must be at least two. Omit it to merge all sealed layers. Existing snapshots remain valid and retain storage until removed.

<Warning>
  Each disk commits separately. If compaction fails, a running VM may stay paused. Restart to recover from disk journals; do not assume rollback or blindly retry.
</Warning>

Use the [SDK references](#reference) for disk selection and result fields. Do not combine compaction with unrelated modifications.

## Verify integrity

Disk content hashing is opt-in. Record hashes during capture, then verify when receiving or checking a snapshot:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { Snapshot } from "microsandbox";

  const snap = await Snapshot.builder("deps")
      .fromSandbox("baseline")
      .recordIntegrity()
      .create();

  const report = await snap.verify();
  ```

  ```rust Rust theme={null}
  use microsandbox::Snapshot;

  let snap = Snapshot::builder("deps")
      .from_sandbox("baseline")
      .record_integrity()
      .create()
      .await?;

  let report = snap.verify().await?;
  ```

  ```python Python theme={null}
  from microsandbox import Snapshot

  snap = await Snapshot.create(
      "deps",
      from_sandbox="baseline",
      record_integrity=True,
  )

  report = await snap.verify()
  ```

  ```go Go theme={null}
  snap, err := m.Snapshot.Create(ctx,
      m.SnapshotCreateOptions{
          Name:            "deps",
          FromSandbox:     "baseline",
          RecordIntegrity: true,
      },
  )

  report, err := snap.Verify(ctx)
  ```

  ```bash CLI theme={null}
  msb snap create deps --sandbox baseline --integrity
  msb snap verify baseline:deps
  msb snap inspect baseline:deps --verify
  ```
</CodeGroup>

Without hashes, size and structure checks cannot detect same-length disk corruption. Save and load preserve recorded integrity but do not scan it automatically. Full-snapshot RAM has separate content-addressed checks; fork RAM is not hashed.

<Accordion title="Full snapshots and forks">
  <CodeGroup>
    ```typescript TypeScript theme={null}
    await Snapshot.builder("saved")
        .fromSandbox("worker")
        .full()
        .recordIntegrity()
        .create();

    const source = await Sandbox.get("worker");
    const child = await source.fork("child", { recordIntegrity: true });
    ```

    ```rust Rust theme={null}
    Snapshot::builder("saved")
        .from_sandbox("worker")
        .full()
        .record_integrity()
        .create()
        .await?;

    let source = Sandbox::get("worker").await?;
    let child = source.fork("child").record_integrity().fork().await?;
    ```

    ```python Python theme={null}
    await Snapshot.create(
        "saved",
        from_sandbox="worker",
        full=True,
        record_integrity=True,
    )

    source = await Sandbox.get("worker")
    child = await source.fork("child", record_integrity=True)
    ```

    ```go Go theme={null}
    var err error

    _, err = m.Snapshot.Create(ctx, m.SnapshotCreateOptions{
        Name: "saved",
        FromSandbox: "worker",
        Full: true,
        RecordIntegrity: true,
    })
    if err != nil { return err }

    source, err := m.GetSandbox(ctx, "worker")
    if err != nil { return err }

    child, err := source.Fork(ctx, "child", m.WithForkIntegrity())
    if err != nil { return err }
    ```

    ```bash CLI theme={null}
    msb snap create saved --sandbox worker --full --integrity
    msb fork worker --name child --integrity
    ```
  </CodeGroup>
</Accordion>

## Troubleshooting

| Problem | What to check |
| - | - |
| Cloud capture is rejected | Stop the source and use disk mode. Full snapshots and local archive options are unsupported. |
| Full restore rejects resource settings | Keep the captured CPU and memory settings, or restore with `--disk-only`. A full snapshot with a tmpfs root must resume execution. |
| A restored app loses its connection | Reconnect clients and retry application connections; live host connections are not captured. |
| Capture fails after saving a snapshot | Inspect the saved snapshot before retrying. The error may mean capture succeeded but the source could not resume. |
| A local snapshot is missing from the list | Run `msb snap reindex` to rebuild the local index. |

## Reference

See each SDK reference for setup, imports, optional controls, and result fields.

* **SDKs:** [TypeScript](/sdk/typescript/snapshots), [Rust](/sdk/rust/snapshots), [Python](/sdk/python/snapshots), and [Go](/sdk/go/snapshots).
* **CLI:** Run `msb snap --help` or `msb snap restore --help` for all options.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.