PLATINUM DOCS

Filesystem

Read, write, search, and watch files inside a sandbox over HTTP.

The filesystem API reads, writes, searches, and watches files inside a sandbox. It works over HTTP — no SSH, no network setup. Use it to move code and data in and out of a running sandbox.

All paths are absolute paths inside the sandbox.

Read and write

The API caps writes at 16 MiB and reads at 256 MiB. See Limits for larger files.

await sbx.files.write("/workspace/app.py", "print('hello')");
const buf = await sbx.files.read("/workspace/app.py");
sbx.files.write("/workspace/app.py", b"print('hello')")
data = sbx.files.read("/workspace/app.py")
pt sandbox cp ./app.py $ID:/workspace/app.py
pt sandbox cp $ID:/workspace/app.py ./app.py
curl -X PUT "$PT_API_URL/v1/sandboxes/$ID/files?path=/workspace/app.py&mode=0755" \
  -H "Authorization: Bearer $PT_TOKEN" --data-binary @app.py
curl "$PT_API_URL/v1/sandboxes/$ID/files?path=/workspace/app.py" \
  -H "Authorization: Bearer $PT_TOKEN" -o app.py

Writes create parent directories automatically. There is no batch upload — send one file per call.

List, stat, mkdir

const entries = await sbx.files.list("/workspace");
const st = await sbx.files.stat("/workspace/app.py");
const there = await sbx.files.exists("/workspace/app.py");
await sbx.files.mkdir("/workspace/data");
entries = sbx.files.list("/workspace")
st = sbx.files.stat("/workspace/app.py")
there = sbx.files.exists("/workspace/app.py")
sbx.files.mkdir("/workspace/data")
pt sandbox fs ls $ID /workspace
pt sandbox fs stat $ID /workspace/app.py
pt sandbox fs mkdir $ID /workspace/data
curl "$PT_API_URL/v1/sandboxes/$ID/files/list?path=/workspace" -H "Authorization: Bearer $PT_TOKEN"
curl "$PT_API_URL/v1/sandboxes/$ID/files/stat?path=/workspace/app.py" -H "Authorization: Bearer $PT_TOKEN"
curl -X POST "$PT_API_URL/v1/sandboxes/$ID/files/mkdir?path=/workspace/data" -H "Authorization: Bearer $PT_TOKEN"

exists returns a boolean and does not throw. Over REST, use stat — a 404 means the path does not exist.

Delete

Non-empty directories need recurse. The API refuses to delete /.

await sbx.files.delete("/tmp/junk");
await sbx.files.delete("/tmp/junkdir", { recurse: true });
sbx.files.delete("/tmp/junk")
sbx.files.delete("/tmp/junkdir", recurse=True)
pt sandbox fs rm $ID /tmp/junk
pt sandbox fs rm $ID /tmp/junkdir -r
curl -X DELETE "$PT_API_URL/v1/sandboxes/$ID/files?path=/tmp/junkdir&recurse=true" \
  -H "Authorization: Bearer $PT_TOKEN"

Search and replace

In replace, find is a regular expression. Escape metacharacters to match them literally.

const files = await sbx.files.find("/workspace", "*.py");
const hits  = await sbx.files.grep("/workspace", "TODO");
await sbx.files.replace("/workspace", "TODO", "DONE", { glob: "*.py" });
files = sbx.files.find("/workspace", "*.py")
hits = sbx.files.grep("/workspace", "TODO")
sbx.files.replace("/workspace", "TODO", "DONE", glob="*.py")
pt sandbox fs find $ID '*.py' --path /workspace
pt sandbox fs grep $ID TODO --path /workspace
pt sandbox fs replace $ID TODO DONE --path /workspace --glob '*.py'
curl "$PT_API_URL/v1/sandboxes/$ID/files/find?path=/workspace&pattern=*.py&max=500" -H "Authorization: Bearer $PT_TOKEN"
curl "$PT_API_URL/v1/sandboxes/$ID/files/grep?path=/workspace&pattern=TODO&max=500" -H "Authorization: Bearer $PT_TOKEN"
curl -X POST "$PT_API_URL/v1/sandboxes/$ID/files/replace" -H "Authorization: Bearer $PT_TOKEN" \
  -H "Content-Type: application/json" -d '{"path": "/workspace", "find": "TODO", "replace": "DONE", "glob": "*.py"}'

find matches filename globs — anything find -name accepts. grep returns one file:line:content line per match. For large trees, run ripgrep via exec if your template ships it.

Watch for changes

watch streams file-change events under a path as Server-Sent Events.

The stream is best-effort. It does not report deletions. A change in the same second as a poll can slip by. Consume it server-side — a browser EventSource cannot send the auth header.

for await (const ev of sbx.files.watch("/workspace", { maxSeconds: 300 })) {
  if (ev.event === "change") console.log(ev.data.path);
}
for ev in sbx.files.watch("/workspace", max_seconds=300):
    if ev["event"] == "change":
        print(ev["data"]["path"])
pt sandbox fs watch $ID /workspace --max-seconds 300
curl -N "$PT_API_URL/v1/sandboxes/$ID/files/watch?path=/workspace&interval_ms=1000&max_seconds=300" \
  -H "Authorization: Bearer $PT_TOKEN"

ready fires once, after the watcher arms the baseline. The stream reports only changes after that point. The stream ends after max_seconds — reconnect to keep watching.

Limits

LimitValue
Upload (PUT /files)16 MiB per request
Download (GET /files)256 MiB per request
RPC replies (stat, list, find, grep)16 MiB per call

Every write replaces the whole file. There is no append, so one path cannot be uploaded in chunks. For files past the upload limit, fetch them from inside the sandbox with wget/curl via exec. Or stage them on a volume.

File calls reset the auto-stop idle timer. Steady traffic keeps an ephemeral sandbox alive.

Behavior details

The async Python client (AsyncPlatinum) mirrors sbx.files with awaited calls.

Exact request and response schemas are in the API reference.

Browse from the dashboard

The sandbox detail view in the dashboard has a Files tab — browse, upload, download.

See also

  • Exec — run commands and code, redirect large output to files
  • Volumes — persistent storage that survives sandbox deletion
  • API reference — exact request/response schemas