Skip to main content
Direct volume paths are absolute volume-internal paths rooted at /. Relative paths are rejected, .. cannot escape the volume root, and there is no sandbox cwd or user context for direct volume operations.

Read

from nullspace import Volume

volume = Volume.from_name("team-data", create_if_missing=True)

text = volume.files.read("/datasets/report.txt")
raw = volume.files.read("/datasets/report.bin", format="bytes")
Use cat for UTF-8 text. Use read --encoding base64 for binary data.

Write

volume.files.make_dir("/datasets")
volume.files.write("/datasets/report.txt", "ready\n")
volume.files.write("/datasets/blob.bin", b"\x00\x01")

Batch Write

from nullspace import BatchWriteError

try:
    volume.files.write_files([
        ("/datasets/a.txt", "a\n"),
        ("/datasets/b.txt", "b\n"),
    ])
except BatchWriteError as exc:
    print(exc.successes, exc.failures)
write_files() can partially succeed before raising BatchWriteError. Inspect successes and failures before retrying.

Move, Remove, And Permissions

volume.files.rename("/datasets/report.txt", "/datasets/report-old.txt")
volume.files.set_permissions("/datasets/report-old.txt", "0644")
volume.files.remove("/datasets/report-old.txt")

Search And Replace

matches = volume.files.find_files("/datasets", "ready")
paths = volume.files.search_files("/datasets", "*.txt")
result = volume.files.replace_in_files(paths, "ready", "done", dry_run=True)
print(result.dry_run, result.replacements, result.files_modified)
find_files() searches file contents. search_files() matches file names. VolumeReplaceResult includes replacement counts and matched files, so dry runs can show planned changes before writing.

Watch

def on_event(event):
    print(event.type, event.path)

with volume.files.watch_dir("/", on_event=on_event, recursive=True):
    volume.files.write("/datasets/changed.txt", "changed\n")
Direct watches are opened against a concrete volume ID. If you look up a volume by name, the SDK resolves it first and then watches that ID. In multi-host deployments, mutations through another data-plane host may require a resync; clients should treat resync_required as a prompt to list or stat the watched tree again.

Async

from nullspace import AsyncVolume

volume = await AsyncVolume.from_name("team-data", create_if_missing=True)
await volume.files.write("/hello.txt", "persistent\n")
print(await volume.files.read("/hello.txt"))