diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24904de..77c20ac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,11 +2,14 @@
## Unreleased
+- Added checkpoint create, list, restore, and delete operations scoped
+ to a running sandbox through `SandboxInstance.Checkpoints`.
- Added reusable disk and full snapshot create, get, list, and delete operations,
plus sandbox restoration through `CreateSandboxRequest.SnapshotId`.
- Added sandbox labels to create and response models, AND-matched label
filtering in `ListSandboxesAsync()`, and label merging through
`SandboxInstance.SetLabelsAsync()`.
+- Set the next NuGet version and default user agent to `0.1.4`.
## [0.1.3](https://github.com/NodeOps-app/createos-csharp-sdk/releases/tag/v0.1.3) — 2026-09-21
diff --git a/CreateOS.Sandbox.sln b/CreateOS.Sandbox.sln
index 4e52b6a..c396916 100644
--- a/CreateOS.Sandbox.sln
+++ b/CreateOS.Sandbox.sln
@@ -26,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Network", "examples\Network
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Snapshots", "examples\Snapshots\Snapshots.csproj", "{A1000010-0000-4000-8000-000000000010}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Checkpoints", "examples\Checkpoints\Checkpoints.csproj", "{A1000011-0000-4000-8000-000000000011}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreateOS.Sandbox.Tests", "tests\CreateOS.Sandbox.Tests\CreateOS.Sandbox.Tests.csproj", "{B2000001-0000-4000-8000-000000000001}"
EndProject
Global
@@ -82,6 +84,10 @@ Global
{A1000010-0000-4000-8000-000000000010}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1000010-0000-4000-8000-000000000010}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1000010-0000-4000-8000-000000000010}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A1000011-0000-4000-8000-000000000011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A1000011-0000-4000-8000-000000000011}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A1000011-0000-4000-8000-000000000011}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A1000011-0000-4000-8000-000000000011}.Release|Any CPU.Build.0 = Release|Any CPU
{B2000001-0000-4000-8000-000000000001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2000001-0000-4000-8000-000000000001}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2000001-0000-4000-8000-000000000001}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/README.md b/README.md
index 545262c..94df844 100644
--- a/README.md
+++ b/README.md
@@ -120,6 +120,28 @@ capture requires a running source sandbox with no attached external disks.
Each account may retain up to five snapshots across both types. See the runnable
[snapshot example](examples/Snapshots/README.md).
+## Checkpoint a directory
+
+Checkpoints keep local file history inside one running sandbox. Stop
+writers before create or restore; processes are not paused automatically.
+
+```csharp
+var checkpoint = await sandbox.Checkpoints.CreateAsync(
+ new CreateCheckpointRequest
+ {
+ Path = "/workspace/project",
+ Name = "before-update",
+ });
+
+var checkpoints = await sandbox.Checkpoints.ListAsync();
+var restored = await sandbox.Checkpoints.RestoreAsync(checkpoint.Id);
+await sandbox.Checkpoints.DeleteAsync(checkpoint.Id);
+```
+
+Restore always uses the checkpoint's original path. Checkpoints remain on the
+sandbox disk and disappear with it. See the runnable
+[checkpoint example](examples/Checkpoints/README.md).
+
## Delegate access to one sandbox
An owner can create one delegated token for a sandbox. Plaintext is returned
diff --git a/examples/Checkpoints/Checkpoints.csproj b/examples/Checkpoints/Checkpoints.csproj
new file mode 100644
index 0000000..6b512ec
--- /dev/null
+++ b/examples/Checkpoints/Checkpoints.csproj
@@ -0,0 +1 @@
+
diff --git a/examples/Checkpoints/Program.cs b/examples/Checkpoints/Program.cs
new file mode 100644
index 0000000..39f41a5
--- /dev/null
+++ b/examples/Checkpoints/Program.cs
@@ -0,0 +1,44 @@
+using CreateOS.Sandbox;
+
+const string checkpointPath = "/workspace/checkpoint-demo";
+
+using var client = new SandboxClient();
+var sandbox = await client.CreateSandboxAsync(new CreateSandboxRequest
+{
+ Shape = "s-1vcpu-1gb",
+ RootFileSystem = "devbox:1"
+});
+Checkpoint? checkpoint = null;
+
+try
+{
+ await RunAsync(sandbox, $"mkdir -p {checkpointPath} && printf 'version one' > {checkpointPath}/value");
+ checkpoint = await sandbox.Checkpoints.CreateAsync(new CreateCheckpointRequest
+ {
+ Path = checkpointPath,
+ Name = "before-update"
+ });
+ await RunAsync(sandbox, $"printf 'version two' > {checkpointPath}/value");
+ await sandbox.Checkpoints.RestoreAsync(checkpoint.Id);
+ var result = await RunAsync(sandbox, $"cat {checkpointPath}/value");
+ Console.WriteLine($"Restored {checkpoint.Id}: {result.Result.StandardOutput}");
+}
+finally
+{
+ try
+ {
+ if (checkpoint is not null)
+ await sandbox.Checkpoints.DeleteAsync(checkpoint.Id, CancellationToken.None);
+ }
+ finally
+ {
+ await sandbox.DestroyAsync(CancellationToken.None);
+ }
+}
+
+static Task RunAsync(SandboxInstance sandbox, string script) =>
+ sandbox.RunCommandAsync(new RunCommandRequest
+ {
+ Command = "sh",
+ Arguments = ["-c", script]
+ });
diff --git a/examples/Checkpoints/README.md b/examples/Checkpoints/README.md
new file mode 100644
index 0000000..b461b17
--- /dev/null
+++ b/examples/Checkpoints/README.md
@@ -0,0 +1,12 @@
+# Checkpoints
+
+Capture a directory, modify it, restore its earlier contents, and remove the
+checkpoint.
+
+```sh
+export CREATEOS_API_KEY="your-api-key"
+dotnet run --project examples/Checkpoints/Checkpoints.csproj
+```
+
+The example creates live resources and cleans them up when it finishes. Stop
+all writers before creating or restoring a checkpoint.
diff --git a/examples/README.md b/examples/README.md
index e3472ea..ed84b3d 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -27,6 +27,7 @@ the local project reference. No CreateOS API key belongs in the repository.
| [CommandStreaming](CommandStreaming/README.md) | Read command output as it arrives |
| [FilesAndSnapshots](FilesAndSnapshots/README.md) | Upload/download files and snapshot a sandbox |
| [Snapshots](Snapshots/README.md) | Capture and restore a reusable sandbox snapshot |
+| [Checkpoints](Checkpoints/README.md) | Capture and restore local directory history |
| [IngressPreview](IngressPreview/README.md) | Expose a service through an ingress preview URL |
| [Network](Network/README.md) | Connect sandboxes on a private network |
| [CustomTemplate](CustomTemplate/README.md) | Build and use a custom template |
diff --git a/src/CreateOS.Sandbox/CheckpointsService.cs b/src/CreateOS.Sandbox/CheckpointsService.cs
new file mode 100644
index 0000000..638f78d
--- /dev/null
+++ b/src/CreateOS.Sandbox/CheckpointsService.cs
@@ -0,0 +1,39 @@
+using System.Text.Json;
+using CreateOS.Sandbox.Internal;
+
+namespace CreateOS.Sandbox;
+
+/// Manages local checkpoints inside one running sandbox.
+public sealed class CheckpointsService
+{
+ private readonly SandboxInstance _instance;
+ private readonly Transport _transport;
+
+ internal CheckpointsService(SandboxInstance instance, Transport transport)
+ {
+ _instance = instance;
+ _transport = transport;
+ }
+
+ /// Captures an eligible absolute directory.
+ public async Task CreateAsync(CreateCheckpointRequest request, CancellationToken cancellationToken = default) =>
+ await _transport.SendAsync(HttpMethod.Post, Path(), request, cancellationToken: cancellationToken).ConfigureAwait(false)
+ ?? throw new JsonException("Checkpoint create response contained no data.");
+
+ /// Lists checkpoints oldest first.
+ public async Task> ListAsync(CancellationToken cancellationToken = default) =>
+ await _transport.SendAsync(HttpMethod.Get, Path(), cancellationToken: cancellationToken).ConfigureAwait(false)
+ ?? [];
+
+ /// Restores a checkpoint at its original directory path.
+ public async Task RestoreAsync(string checkpointId, CancellationToken cancellationToken = default) =>
+ await _transport.SendAsync(HttpMethod.Post, Path(checkpointId, "/restore"), cancellationToken: cancellationToken).ConfigureAwait(false)
+ ?? throw new JsonException("Checkpoint restore response contained no data.");
+
+ /// Deletes a checkpoint record while retaining referenced layers.
+ public async Task DeleteAsync(string checkpointId, CancellationToken cancellationToken = default) =>
+ _ = await _transport.SendAsync