Tide

Virtual file system

The sandboxed in-memory VFS and its file operations.

All file operations in a workflow run against a sandboxed, in-memory virtual file system (VFS) — never the host machine's filesystem. They are global functions, no imports needed.

Type definitions are in runtime/src/tide.d.ts for editor autocompletion.

How the VFS works

  • Isolation. Each invocation has its own independent VFS; workflows cannot see each other's files or reach the host.
  • Derived from the journal. There is no separate VFS snapshot file. On first run the VFS starts empty and each writeFile / removeFile records a journal entry; on replay it starts empty again and the write/remove entries re-apply in order to rebuild the exact same state.
  • Step snapshots. When a step begins, Tide takes an in-memory snapshot of the VFS. If the step fails, the VFS is restored from that snapshot — only an in-memory mechanism, never persisted.
  • Paths. Paths are plain strings relative to the VFS root; intermediate directories are created on write. No drive letters, symlinks, or permissions.

The host filesystem is never touched — see Security for why.

readFile

readFile(path: string): Promise<string>

Reads a file from the virtual filesystem.

Parameters:

  • path -- Path to the file in the VFS

Returns: The file contents as a UTF-8 string.

Throws: If the file does not exist.

Behavior:

  • First run: Reads from the in-memory VFS and records the result in the journal
  • Replay: Returns the journaled result without accessing the VFS
  • Inside a step: Reads see the current in-memory VFS state, including writes made earlier in the same step. The read result is buffered as a journal entry and committed atomically when the step completes.
await writeFile("config.json", '{"key": "value"}');
const data = await readFile("config.json");
log(data); // '{"key": "value"}'

readFileBytes

readFileBytes(path: string): Promise<Uint8Array>

Reads binary data from the virtual filesystem.

Parameters:

  • path -- Path to the file in the VFS

Returns: The file contents as a Uint8Array.

Throws: If the file does not exist.

Behavior:

  • First run: Reads from the in-memory VFS and records the result in the journal (stored as base64)
  • Replay: Returns the journaled result without accessing the VFS
  • Inside a step: Reads see the current in-memory VFS state, including writes made earlier in the same step. The read result is buffered as a journal entry and committed atomically when the step completes.
await writeFileBytes("image.png", pngData);
const bytes = await readFileBytes("image.png");
console.log(bytes instanceof Uint8Array); // true

writeFile

writeFile(path: string, contents: string): Promise<void>

Writes a text file to the virtual filesystem.

Parameters:

  • path -- Path to the file in the VFS
  • contents -- String contents to write

Returns: void

Behavior:

  • Creates the file if it doesn't exist, overwrites if it does
  • Mutates the in-memory VFS immediately (subsequent reads see the new content)
  • Outside a step: The journal entry is committed to storage immediately
  • Inside a step: The journal entry is buffered and committed atomically when the step completes. If the step fails, the VFS is rolled back to its pre-step state and the buffered entry is discarded.
  • Replay: The VFS mutation is re-applied from the journal to rebuild state; the journaled result (null) is returned
await writeFile("output.txt", "Hello, world!");
await writeFile("output.txt", "Overwritten!"); // Replaces the file

const contents = await readFile("output.txt");
console.log(contents); // "Overwritten!"

writeFileBytes

writeFileBytes(path: string, contents: Uint8Array): Promise<void>

Writes binary data to the virtual filesystem.

Parameters:

  • path -- Path to the file in the VFS
  • contents -- Binary contents to write as a Uint8Array

Returns: void

Behavior:

  • Creates the file if it doesn't exist, overwrites if it does
  • Mutates the in-memory VFS immediately (subsequent reads see the new content)
  • Outside a step: The journal entry is committed to storage immediately
  • Inside a step: The journal entry is buffered and committed atomically when the step completes. If the step fails, the VFS is rolled back to its pre-step state and the buffered entry is discarded.
  • Replay: The VFS mutation is re-applied from the journal to rebuild state; the journaled result (null) is returned
const data = new Uint8Array([0x89, 0x50, 0x4e, 0x47]); // PNG header
await writeFileBytes("image.png", data);

const readBack = await readFileBytes("image.png");
console.log(readBack[0]); // 137 (0x89)

removeFile

removeFile(path: string): Promise<void>

Removes a file from the virtual filesystem.

Parameters:

  • path -- Path to the file in the VFS

Returns: void

Behavior:

  • Deletes the file from the in-memory VFS
  • Does not throw if the file does not exist
  • Outside a step: The journal entry is committed to storage immediately
  • Inside a step: The journal entry is buffered and committed atomically when the step completes. If the step fails, the VFS is rolled back to its pre-step state and the removed file is restored.
  • Replay: The VFS mutation is re-applied from the journal to rebuild state; the journaled result is returned
await writeFile("temp.txt", "temporary");
await removeFile("temp.txt");
// File no longer exists in VFS

listFiles

listFiles(path?: string): Promise<Array<{ name: string; isFile: boolean }>>

Lists the contents of a directory in the virtual filesystem.

Parameters:

  • path -- (optional) Path to the directory in the VFS. Defaults to the VFS root when omitted.

Returns: An array of entries, each with:

  • name -- The name of the file or directory
  • isFile -- true if the entry is a file, false if it is a directory

Behavior:

  • First run: Reads the directory listing from the in-memory VFS and records the result in the journal
  • Replay: Returns the journaled result for deterministic replay
  • Inside a step: Reads see the current in-memory VFS state. The listing result is buffered as a journal entry and committed atomically when the step completes.
await writeFile("docs/readme.txt", "Hello");
await writeFile("docs/notes.txt", "World");

const entries = await listFiles("docs");
console.log(entries);
// [{ name: "readme.txt", isFile: true }, { name: "notes.txt", isFile: true }]

const root = await listFiles();
console.log(root);
// [{ name: "docs", isFile: false }]

Journal Behavior Summary

All file operations are journaled. The table below summarizes what each operation stores and how it behaves during replay:

OperationJournal opResult storedVFS mutation on replay
readFileop_read_fileFile contents (string)No
readFileBytesop_read_file_bytesFile contents (base64)No
writeFileop_write_filenullYes -- re-applies write
writeFileBytesop_write_file_bytesnullYes -- re-applies write
removeFileop_remove_filenullYes -- re-applies removal
listFilesop_list_filesArray of entriesNo

Read operations (readFile, readFileBytes, listFiles) return their journaled result on replay without touching the VFS. Write operations (writeFile, writeFileBytes, removeFile) re-apply their mutations to rebuild the in-memory VFS state during replay.

File Paths

VFS paths are simple strings. There is no concept of a working directory -- all paths are treated relative to the VFS root:

await writeFile("hello.txt", "data");           // /hello.txt
await writeFile("subdir/file.txt", "data");     // /subdir/file.txt
await readFile("hello.txt");                     // reads /hello.txt

Intermediate directories are created automatically when writing to a nested path. The VFS does not support absolute paths with drive letters, symbolic links, or file permissions.

  • Steps — atomic transactions with VFS rollback.
  • Durable execution — how journaling and replay work.
  • Security — why the VFS is sandboxed from the host.

On this page