# Volume Fork

Volume Fork creates a new Volume from an existing Volume using s0fs Copy-on-Write (COW) clone.

<Callout variant="info">
Volume fork is supported only for the `s0fs` backend. Forking an `s3` backend Volume returns a bad request because the external bucket prefix remains the source of truth. See [Volume Backends](/docs/sandbox/volume/backends) for the compatibility matrix.
</Callout>

## Why Use Fork?

- **Fast Clone**: Create a new Volume quickly without copying all data blocks
- **Storage Efficient**: Source and fork share unchanged data blocks
- **Write Isolation**: Writes to the fork do not affect the source Volume
- **Great for Branching**: Create per-task or per-experiment data branches

## How Fork Works

```mermaid
flowchart TB
    src["Source Volume (vol_src)<br/>/data/a.txt<br/>/data/b.txt"]
    fork["Forked Volume (vol_fork)<br/>/data/a.txt (shared blocks)<br/>/data/b.txt (shared blocks)"]
    wfork["After write: vol_fork /data/b.txt -> new blocks"]
    wsrc["vol_src /data/b.txt -> unchanged"]

    src -->|"Fork (COW clone)"| fork
    fork --> wfork
    src --> wsrc
```

## API

- **Endpoint**: `POST /api/v1/sandboxvolumes/{id}/fork`
- **Path Parameter**: `id` is the source Volume ID
- **Request Body**: Optional, used to set the forked Volume access mode
- **Success Response**: `201 Created`, returns the new `SandboxVolume` including `source_volume_id`
- **Common Errors**: `400` (invalid `access_mode`), `404` (source Volume not found or not visible to your team)

## Optional Request Fields

| Field | Type | Description |
|------|------|-------------|
| `access_mode` | enum | Access mode for the forked Volume. If omitted, defaults to `RWO` |

## Fork a Volume

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `forked, err := client.ForkVolume(ctx, volume.ID, &apispec.ForkVolumeRequest{
    AccessMode: apispec.NewOptVolumeAccessMode(apispec.VolumeAccessModeRWO),
})
if err != nil {
    panic(err)
}

fmt.Printf("Forked Volume: %s\n", forked.ID)
if sourceVolumeID, ok := forked.SourceVolumeID.Get(); ok {
    fmt.Printf("Source Volume: %s\n", sourceVolumeID)
} else {
    fmt.Println("Source Volume: <nil>")
}`
    },
    {
      label: "Python",
      language: "python",
      code: `from sandbox0.apispec.models.fork_volume_request import ForkVolumeRequest
from sandbox0.apispec.types import Unset

forked = client.volumes.fork(
    volume.id,
    ForkVolumeRequest(
        access_mode=VolumeAccessMode.RWO,
    ),
)

print(f"Forked Volume: {forked.id}")
source_volume_id = forked.source_volume_id
if isinstance(source_volume_id, Unset) or source_volume_id is None:
    print("Source Volume: <nil>")
else:
    print(f"Source Volume: {source_volume_id}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `const forked = await client.volumes.fork(volume.id, {
    accessMode: models.VolumeAccessMode.Rwo,
});

console.log("Forked Volume:", forked.id);
if (forked.sourceVolumeId) {
    console.log("Source Volume:", forked.sourceVolumeId);
} else {
    console.log("Source Volume: <nil>");
}`
    },
    {
      label: "CLI",
      language: "bash",
      code: `# Fork a volume with default settings
s0 volume fork <source-volume-id>

# Fork with an explicit access mode
s0 volume fork <source-volume-id> \\
    --access-mode RWO`
    }
  ]}
/>

## Verify Isolation

1. Mount both the source and the forked Volume to a Sandbox at different paths.
2. Modify a file only in the forked Volume.
3. Read the same file from the source Volume and verify it is unchanged.

<Callout variant="info">
Fork uses COW at the filesystem metadata layer, so unchanged data is shared while changed data is isolated.
</Callout>

<Callout variant="info">
Fork does not require the source Volume to be mounted.
</Callout>

---

## Next Steps

<CardGroup>
  <Card title="Backends" href="/docs/sandbox/volume/backends" cta="Continue">
    Check which Volume operations are available for each backend.
  </Card>

  <Card title="Snapshots" href="/docs/sandbox/volume/snapshots" cta="Continue">
    Create restore points before branching Volume state.
  </Card>
</CardGroup>
