# Files

Manage files and directories within a sandbox. The Files API provides full POSIX file operations including read, write, delete, move, and real-time file watching.

Files written to the sandbox writable filesystem are restored across checkpointed pause/resume for the same sandbox identity, including paths that are not backed by mounted Volumes. They are deleted with the sandbox identity unless you publish a rootfs snapshot or fork before cleanup. Use [Snapshot And Restore](/docs/sandbox/snapshot-restore) for sandbox rootfs checkpoints, restore, and fork operations. Use [Volumes](/docs/sandbox/volume) for data that must be shared across sandboxes or accessed directly without a running sandbox.

## File Model

### FileInfo

| Field | Type | Description |
|-------|------|-------------|
| `name` | string | File name |
| `path` | string | Full path |
| `type` | string | File type: `file`, `dir`, `symlink` |
| `size` | integer | File size in bytes |
| `mode` | string | Unix file mode (e.g., `0755`) |
| `mod_time` | string | Last modification time (ISO 8601) |
| `is_link` | boolean | Whether it's a symlink |
| `link_target` | string | Symlink target path (if applicable) |

---

## Write File

Write content to a file. Creates the file if it doesn't exist.

<Endpoint method="POST">
/api/v1/sandboxes/{'{id}'}/files
</Endpoint>

### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `path` | string | File path to write (required) |

### Request Body

Binary file content (raw bytes).

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Write file content
content := []byte("Hello from Sandbox0!\\n")
_, err := sandbox.WriteFile(ctx, "/workspace/hello.txt", content)
if err != nil {
    log.Fatal(err)
}
fmt.Println("File written successfully")`
    },
    {
      label: "Python",
      language: "python",
      code: `# Write file content
sandbox.write_file("/workspace/hello.txt", b"Hello from Sandbox0!\\n")
print("File written successfully")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// Write file content
await sandbox.writeFile('/workspace/hello.txt', 'Hello from Sandbox0!\\n');
console.log('File written successfully');`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox files write /workspace/hello.txt --data "Hello from Sandbox0!" -s sb_abc123`
    }
  ]}
/>

---

## Read File

Read file content from a sandbox.

<Endpoint method="GET">
/api/v1/sandboxes/{'{id}'}/files
</Endpoint>

### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `path` | string | File path to read (required) |

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Read the file
readResult, err := sandbox.ReadFile(ctx, "/workspace/hello.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("File content: %s\\n", strings.TrimSpace(string(readResult)))`
    },
    {
      label: "Python",
      language: "python",
      code: `# Read file content
content = sandbox.read_file("/workspace/hello.txt")
print(f"Content: {content.decode()}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// Read file content
var content = await sandbox.readFile('/workspace/hello.txt');
console.log('Content:', Buffer.from(content).toString('utf-8'));`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox files cat /workspace/hello.txt -s sb_abc123

# Output:
# Hello from Sandbox0!`
    }
  ]}
/>

---

## Create Directory

Create a new directory.

<Endpoint method="POST">
/api/v1/sandboxes/{'{id}'}/files
</Endpoint>

### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `path` | string | Directory path to create (required) |
| `mkdir` | boolean | Set to `true` to create directory |
| `recursive` | boolean | Create parent directories if needed |

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Create directory (with parent directories)
_, err = sandbox.Mkdir(ctx, "/workspace/project/src", true)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Directory created")`
    },
    {
      label: "Python",
      language: "python",
      code: `# Create directory (with parent directories)
sandbox.mkdir("/workspace/project/src", recursive=True)
print("Directory created")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// Create directory (with parent directories)
await sandbox.mkdir('/workspace/project/src', true);
console.log('Directory created');`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox files mkdir /workspace/project/src --parents -s sb_abc123`
    }
  ]}
/>

---

## Move File or Directory

Move or rename a file or directory.

<Endpoint method="POST">
/api/v1/sandboxes/{'{id}'}/files/move
</Endpoint>

### Request Body

| Field | Type | Description |
|-------|------|-------------|
| `source` | string | Source path |
| `destination` | string | Destination path |

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Move/rename file
_, err = sandbox.MoveFile(ctx, "/workspace/hello.txt", "/workspace/new-hello.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Println("File moved")`
    },
    {
      label: "Python",
      language: "python",
      code: `# Move/rename file
sandbox.move_file("/workspace/hello.txt", "/workspace/new-hello.txt")
print("File moved")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// Move/rename file
await sandbox.moveFile('/workspace/hello.txt', '/workspace/new-hello.txt');
console.log('File moved');`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox files mv /workspace/hello.txt /workspace/new-hello.txt -s sb_abc123`
    }
  ]}
/>

---

## Delete File or Directory

Delete a file or directory.

<Endpoint method="DELETE">
/api/v1/sandboxes/{'{id}'}/files
</Endpoint>

### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `path` | string | Path to delete (required) |

<Callout variant="warning">
Deleting a directory removes all its contents recursively. This action cannot be undone.
</Callout>

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Delete file
_, err = sandbox.DeleteFile(ctx, "/workspace/new-hello.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Println("File deleted")`
    },
    {
      label: "Python",
      language: "python",
      code: `# Delete file
sandbox.delete_file("/workspace/new-hello.txt")
print("File deleted")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// Delete file
await sandbox.deleteFile('/workspace/new-hello.txt');
console.log('File deleted');`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox files rm /workspace/new-hello.txt -s sb_abc123`
    }
  ]}
/>

---

## Get File Info (Stat)

Get metadata about a file or directory.

<Endpoint method="GET">
/api/v1/sandboxes/{'{id}'}/files/stat
</Endpoint>

### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `path` | string | File path to stat (required) |

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Get file info
info, err := sandbox.StatFile(ctx, "/workspace/hello.txt")
if err != nil {
    log.Fatal(err)
}
if name, ok := info.Name.Get(); ok {
    fmt.Printf("Name: %s\\n", name)
}
if size, ok := info.Size.Get(); ok {
    fmt.Printf("Size: %d bytes\\n", size)
}
if fileType, ok := info.Type.Get(); ok {
    fmt.Printf("Type: %s\\n", fileType)
}
if mode, ok := info.Mode.Get(); ok {
    fmt.Printf("Mode: %s\\n", mode)
}`
    },
    {
      label: "Python",
      language: "python",
      code: `sandbox.write_file("/workspace/hello.txt", b"Hello from Sandbox0!\\n")
# Get file info
info = sandbox.stat_file("/workspace/hello.txt")
print(f"Name: {info.name}")
print(f"Size: {info.size} bytes")
print(f"Type: {info.type_}")
print(f"Mode: {info.mode}")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `await sandbox.writeFile('/workspace/hello.txt', 'Hello from Sandbox0!\\n');
// Get file info
const info = await sandbox.statFile('/workspace/hello.txt');
console.log('Name:', info.name);
console.log('Size:', info.size, 'bytes');
console.log('Type:', info.type);
console.log('Mode:', info.mode);`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox files write /workspace/hello.txt --data 'Hello From Sandbox0!' -s sb_abc123
s0 sandbox files stat /workspace/hello.txt -s sb_abc123`
    }
  ]}
/>

---

## List Directory

List contents of a directory.

<Endpoint method="GET">
/api/v1/sandboxes/{'{id}'}/files/list
</Endpoint>

### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `path` | string | Directory path to list (required) |

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// List directory contents
entries, err := sandbox.ListFiles(ctx, "/workspace")
if err != nil {
    log.Fatal(err)
}
for _, entry := range entries {
    path, ok := entry.Path.Get()
    if !ok {
        continue
    }
    fileType, _ := entry.Type.Get()
    size, _ := entry.Size.Get()
    fmt.Printf("- %s (%s, %d bytes)\\n", path, fileType, size)
}`
    },
    {
      label: "Python",
      language: "python",
      code: `# List directory contents
entries = sandbox.list_files("/workspace")
for entry in entries:
    print(f"- {entry.path} ({entry.type_}, {entry.size} bytes)")`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// List directory contents
var entries = await sandbox.listFiles('/workspace');
for (const entry of entries) {
    console.log(\`- \${entry.path ?? entry.name} (\${entry.type ?? 'unknown'}, \${entry.size ?? 0} bytes)\`);
}`
    },
    {
      label: "CLI",
      language: "bash",
      code: `s0 sandbox files ls /workspace -s sb_abc123`
    }
  ]}
/>

---

## Watch Files

Watch a directory for file system changes in real-time via WebSocket.

<Endpoint method="GET">
/api/v1/sandboxes/{'{id}'}/files/watch
</Endpoint>

### WebSocket Protocol

**Client Messages:**

| Action | Description | Example |
|--------|-------------|---------|
| `subscribe` | Subscribe to a path | `{"action": "subscribe", "path": "/tmp", "recursive": true}` |
| `unsubscribe` | Unsubscribe from a watch | `{"action": "unsubscribe", "watch_id": "watch-123"}` |

**Server Messages:**

| Type | Description | Example |
|------|-------------|---------|
| `subscribed` | Subscription confirmed | `{"type": "subscribed", "watch_id": "watch-123", "path": "/tmp"}` |
| `event` | File event occurred | `{"type": "event", "watch_id": "watch-123", "event": "write", "path": "/tmp/a.txt"}` |
| `unsubscribed` | Unsubscription confirmed | `{"type": "unsubscribed", "watch_id": "watch-123"}` |
| `error` | Error occurred | `{"type": "error", "error": "permission denied"}` |

### File Events

| Event | Description |
|-------|-------------|
| `create` | File or directory created |
| `write` | File content modified |
| `rename` | File or directory renamed |
| `chmod` | File permissions changed |
| `remove` | File or directory deleted |

<Tabs
  tabs={[
    {
      label: "Go",
      language: "go",
      code: `// Watch files for changes
watchCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()

events, errs, unsubscribe, err := sandbox.WatchFiles(watchCtx, "/workspace", true)
if err != nil {
    log.Fatal(err)
}
defer func() {
    if err := unsubscribe(); err != nil {
        log.Printf("cleanup unsubscribe watch: %v", err)
    }
}()

// Process events
for {
    select {
    case ev, ok := <-events:
        if !ok {
            return
        }
        fmt.Printf("Event: %s on %s\\n", ev.Event, ev.Path)
    case err, ok := <-errs:
        if ok && err != nil {
            log.Printf("Watch error: %v", err)
        }
    case <-watchCtx.Done():
        fmt.Println("Watch timeout")
        return
    }
}`
    },
    {
      label: "Python",
      language: "python",
      code: `# Watch files for changes
stream = sandbox.watch_files("/workspace", recursive=True)

try:
    for event in stream.iter_events():
        print(f"Event: {event.event} on {event.path}")
        if event.event == "write":
            print(f"File modified: {event.path}")
except KeyboardInterrupt:
    pass
finally:
    stream.unsubscribe()`
    },
    {
      label: "TypeScript",
      language: "typescript",
      code: `// Watch files for changes
const watcher = await sandbox.watchFiles('/workspace', true);

const stopTimer = setTimeout(() => {
    if (watcher.watchId) {
        watcher.unsubscribe(watcher.watchId);
    } else {
        watcher.close();
    }
}, 30000);

try {
    for await (const event of watcher.events()) {
        if (event.type === 'error') {
            console.error('Watch error:', event.error);
            break;
        }
        if (event.type === 'event') {
            console.log(\`Event: \${event.event} on \${event.path}\`);
            if (event.event === 'write') {
                console.log(\`File modified: \${event.path}\`);
            }
        }
    }
} finally {
    watcher.close();
}`
    },
    {
      label: "CLI",
      language: "bash",
      code: `# Watch files for changes
s0 sandbox files watch /workspace --recursive -s sb_abc123`
    }
  ]}
/>

---

## Next Steps

<CardGroup>
  <Card title="SSH" href="/docs/sandbox/ssh" cta="Continue">
    Connect to sandboxes with standard SSH clients for shell and file-copy workflows.
  </Card>

  <Card title="Network" href="/docs/sandbox/network" cta="Continue">
    Configure outbound network policy, traffic rules, and protocol-aware restrictions.
  </Card>
</CardGroup>
