Skip to main content
vtz exposes the Node.js fs API — no proprietary vtz.readFile() or custom global. If you’ve written a Node script, the code you already have works.
Imports go through node:fs (promise-returning) or node:fs/promises (promise namespace); synchronous variants come from node:fs.

Text vs binary

Pass an encoding to get a string; omit it to get a Buffer (which is a Uint8Array subclass).
Accepted encodings include 'utf-8', 'utf8', 'ascii', 'base64', 'hex', 'latin1'. A plain Uint8Array is also a valid write payload.

Async vs sync

Prefer the async forms for request handlers and long-running code — they don’t block the event loop. Use sync variants for startup and scripts where you’d block anyway.

Paths

Always resolve paths relative to a known anchor. process.cwd() depends on where the script was invoked from; import.meta.dirname is stable.
file:// URLs also work anywhere a path is accepted:

Directories

readdir returns names only. Pair with stat() when you need file type or size:

Existence checks

Avoid the check-then-read pattern in hot paths — read and handle ENOENT instead, which is both faster and race-free:

Watching for changes

fs.watch() is polling-based (~500ms interval) — good enough for scripts and dev tools, not suited for high-frequency filesystem monitoring.

What’s not supported

APIStatus
createReadStream / createWriteStreamNot implemented. Read/write the full buffer, or chunk manually with read() / write() on an open fd.
Callback-style async (fs.readFile(path, cb))Not implemented. Use node:fs/promises or readFileSync.
fs.appendFile (async)Not implemented. appendFileSync works; otherwise read → concat → write.
Inode-based watchersPolling only.
If your code relies on streams for a file that might be large, read into a Buffer and process chunks in memory — the runtime doesn’t yet help you backpressure.

Security

vtz deliberately does not sandbox file access. Any path your script names, it can open — the runtime trusts its own code, matching Node and Bun semantics. If you’re executing untrusted input, validate and normalize paths yourself before passing them to fs. For desktop apps with permission-gated file access, see @vertz/desktop → File System.

Implementation

Rust ops live in native/vtz/src/runtime/ops/fs.rs; the node:fs and node:fs/promises synthetic modules are wired in native/vtz/src/runtime/module_loader.rs.